diff --git a/docs/source/nitpick-exceptions b/docs/source/nitpick-exceptions index 033cccffb6..5b1f3e279f 100644 --- a/docs/source/nitpick-exceptions +++ b/docs/source/nitpick-exceptions @@ -26,14 +26,16 @@ py:class json.encoder.JSONEncoder py:class EXPOSED_TYPE py:class EVENT_CALLBACK_TYPE py:class datetime +py:class UUID py:class types.LambdaType py:meth tempfile.TemporaryDirectory ### AiiDA py:class ReturnType py:class SelfType -py:class CollectionType +py:class EntityCollectionType py:class EntityType +py:class EntityModelType py:class EntityClsType py:class ProjectType py:class BackendEntityType @@ -50,8 +52,9 @@ py:class UserDefinedType py:class LinkAnnotateType py:obj ReturnType py:obj SelfType -py:obj CollectionType +py:obj EntityCollectionType py:obj EntityType +py:obj EntityModelType py:obj BackendEntityType py:obj BackendNodeType py:obj TransactionType @@ -61,21 +64,19 @@ py:class aiida.common.ReturnType py:class aiida.common.SelfType py:class aiida.common.lang.MethodType py:class aiida.cmdline.params._shims.C -py:class aiida.orm.entitites.CollectionType -py:class aiida.orm.entitites.EntityType -py:class aiida.orm.entitites.BackendEntityType py:class aiida.orm.groups.SelfType -py:class aiida.orm.implementation.entitites.EntityType py:class aiida.engine.processes.functions.FunctionType py:class aiida.engine.processes.workchains.workchain.MethodType +py:class aiida.orm.entities.EntityInputModel py:class aiida.orm.entities.EntityType +py:class aiida.orm.entities.EntityModelType py:class aiida.orm.entities.BackendEntityType -py:class aiida.orm.entities.CollectionType +py:class aiida.orm.entities.EntityCollectionType py:class aiida.orm.implementation.nodes.BackendNodeType py:class aiida.orm.implementation.storage_backend.TransactionType py:class aiida.orm.implementation.entities.EntityType py:class aiida.orm.nodes.data.enum.EnumType -py:class aiida.orm.nodes.NodeType +py:class aiida.orm.nodes.node.NodeType py:class aiida.storage.psql_dos.orm.ModelType py:class aiida.storage.psql_dos.orm.SelfType py:class aiida.storage.psql_dos.orm.entities.SelfType @@ -88,18 +89,15 @@ py:class aiida.common.lang.ReturnType py:obj aiida.common.ReturnType py:obj aiida.common.SelfType py:obj aiida.common.lang.ReturnType -py:obj aiida.orm.entitites.CollectionType -py:obj aiida.orm.entitites.EntityType -py:obj aiida.orm.entitites.BackendEntityType py:obj aiida.orm.groups.SelfType -py:obj aiida.orm.entities.CollectionType +py:obj aiida.orm.entities.EntityCollectionType py:obj aiida.orm.entities.BackendEntityType py:obj aiida.orm.entities.EntityType +py:obj aiida.orm.entities.EntityModelType py:obj aiida.orm.implementation.entities.EntityType py:obj aiida.orm.implementation.nodes.BackendNodeType py:obj aiida.orm.implementation.storage_backend.TransactionType py:obj aiida.orm.nodes.node.NodeType -py:obj aiida.orm.nodes.NodeType py:obj aiida.storage.psql_dos.orm.ModelType py:obj aiida.storage.psql_dos.orm.SelfType py:obj aiida.storage.psql_dos.orm.entities.ModelType diff --git a/pyproject.toml b/pyproject.toml index 4a73d7c15c..31ace7b176 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -331,7 +331,7 @@ enable_error_code = [ 'truthy-bool' ] plugins = [ - 'pydantic.mypy', + # 'pydantic.mypy', 'sqlalchemy.ext.mypy.plugin' ] scripts_are_modules = true @@ -480,7 +480,7 @@ select = [ # Mark some classes as generic, per https://docs.astral.sh/ruff/settings/#lint_pyflakes_extend-generics # Needed due to https://github.com/astral-sh/ruff/issues/9298 [tool.ruff.lint.pyflakes] -extend-generics = ["aiida.orm.entities.Entity", "aiida.orm.entities.Collection"] +extend-generics = ["aiida.orm.entities.Entity", "aiida.orm.entities.EntityCollection", "aiida.orm.entities.EntityModel"] [tool.tox] legacy_tox_ini = """ diff --git a/src/aiida/cmdline/commands/cmd_code.py b/src/aiida/cmdline/commands/cmd_code.py index f463b8755c..6c80d3e08d 100644 --- a/src/aiida/cmdline/commands/cmd_code.py +++ b/src/aiida/cmdline/commands/cmd_code.py @@ -8,11 +8,13 @@ ########################################################################### """`verdi code` command.""" +from __future__ import annotations + import pathlib import warnings from collections import defaultdict from functools import partial -from typing import Any +from typing import TYPE_CHECKING, Any import click @@ -26,16 +28,20 @@ from aiida.cmdline.utils.decorators import with_dbenv from aiida.common import exceptions +if TYPE_CHECKING: + from aiida.orm import Code + @verdi.group('code') def verdi_code(): """Setup and manage codes.""" -def create_code(ctx: click.Context, cls, **kwargs) -> None: +def create_code(ctx: click.Context, cls: Code, **kwargs) -> None: """Create a new `Code` instance.""" try: - instance = cls._from_model(cls.Model(**kwargs)) + model = cls.CreateModel(**kwargs) + instance = cls.from_model(model) # type: ignore[arg-type] except (TypeError, ValueError) as exception: echo.echo_critical(f'Failed to create instance `{cls}`: {exception}') @@ -226,7 +232,6 @@ def code_duplicate(ctx, code, non_interactive, **kwargs): def show(code): """Display detailed information for a code.""" from aiida.cmdline import is_verbose - from aiida.common.pydantic import get_metadata table = [] @@ -235,38 +240,21 @@ def show(code): table.append(['UUID', code.uuid]) table.append(['Type', code.entry_point.name]) - for field_name, field_info in code.Model.model_fields.items(): - # Skip fields excluded from CLI - if get_metadata( - field_info, - key='exclude_from_cli', - default=False, - ): + for field_name, field_info in code.CreateModel.model_fields.items(): + # We don't show extras for codes + if field_name == 'extras': continue - # Skip fields that are not stored in the attributes column - # NOTE: this also catches e.g., filepath_files for PortableCode, which is actually a "misuse" - # of the is_attribute metadata flag, as there it is flagging that the field is not stored at all! - # TODO (edan-bainglass) consider improving this by introducing a new metadata flag or reworking PortableCode - # TODO see also Dict and InstalledCode for other potential misuses of is_attribute - if not get_metadata( - field_info, - key='is_attribute', - default=True, - ): + # FIXME resolve this hardcoded special case properly + if field_name == 'filepath_files': continue value = getattr(code, field_name) - # Special handling for computer field to show additional info if field_name == 'computer': value = f'{value.label} ({value.hostname}), pk: {value.pk}' - # Use the field's title as display name. - # This allows for custom titles (class-cased by default from Pydantic). - display_name = field_info.title - - table.append([display_name, value]) + table.append([field_info.title, value]) if is_verbose(): table.append(['Calculations', len(code.base.links.get_outgoing().all())]) diff --git a/src/aiida/cmdline/groups/dynamic.py b/src/aiida/cmdline/groups/dynamic.py index c7949a9065..7b4a7839cf 100644 --- a/src/aiida/cmdline/groups/dynamic.py +++ b/src/aiida/cmdline/groups/dynamic.py @@ -97,8 +97,9 @@ def call_command(self, ctx: click.Context, cls: t.Any, non_interactive: bool, ** if hasattr(cls, 'Model'): # The plugin defines a pydantic model: use it to validate the provided arguments + Model = getattr(cls, 'CreateModel', cls.Model) # noqa: N806 try: - cls.Model(**kwargs) + Model(**kwargs) except ValidationError as exception: param_hint = [ f'--{loc.replace("_", "-")}' # type: ignore[union-attr] @@ -153,8 +154,6 @@ def list_options(self, entry_point: str) -> list[t.Callable[[FC], FC]]: """ from pydantic_core import PydanticUndefined - from aiida.common.pydantic import get_metadata - cls = self.factory(entry_point) if not hasattr(cls, 'Model'): @@ -168,10 +167,13 @@ def list_options(self, entry_point: str) -> list[t.Callable[[FC], FC]]: options_spec = self.factory(entry_point).get_cli_options() # type: ignore[union-attr] return [self.create_option(*item) for item in options_spec] + Model = getattr(cls, 'CreateModel', cls.Model) # noqa: N806 + options_spec = {} - for key, field_info in cls.Model.model_fields.items(): - if get_metadata(field_info, 'exclude_from_cli'): + for key, field_info in Model.model_fields.items(): + # We do not prompt for extras + if key == 'extras': continue default = field_info.default_factory if field_info.default is PydanticUndefined else field_info.default diff --git a/src/aiida/common/datastructures.py b/src/aiida/common/datastructures.py index 58198f051a..94658c09a2 100644 --- a/src/aiida/common/datastructures.py +++ b/src/aiida/common/datastructures.py @@ -167,8 +167,8 @@ class CalcInfo(DefaultFieldsAttributeDict): max_wallclock_seconds: None | int max_memory_kb: None | int rerunnable: bool - retrieve_list: None | list[str | tuple[str, str, str]] - retrieve_temporary_list: None | list[str | tuple[str, str, str]] + retrieve_list: None | list[str | tuple[str, str, int]] + retrieve_temporary_list: None | list[str | tuple[str, str, int]] local_copy_list: None | list[tuple[str, str, str]] remote_copy_list: None | list[tuple[str, str, str]] remote_symlink_list: None | list[tuple[str, str, str]] diff --git a/src/aiida/common/pydantic.py b/src/aiida/common/pydantic.py index 60a3913aad..3646be6ecb 100644 --- a/src/aiida/common/pydantic.py +++ b/src/aiida/common/pydantic.py @@ -3,7 +3,6 @@ from __future__ import annotations import typing as t -from pathlib import Path from pydantic import Field from pydantic_core import PydanticUndefined @@ -34,11 +33,10 @@ def MetadataField( # noqa: N802 priority: int = 0, short_name: str | None = None, option_cls: t.Any | None = None, - orm_class: type[Entity[t.Any, t.Any]] | str | None = None, - orm_to_model: t.Callable[[Entity[t.Any, t.Any], Path], t.Any] | None = None, - model_to_orm: t.Callable[['BaseModel'], t.Any] | None = None, + orm_class: type[Entity[t.Any, t.Any, t.Any]] | str | None = None, + orm_to_model: t.Callable[[Entity[t.Any, t.Any, t.Any], dict[str, t.Any]], t.Any] | None = None, + model_to_orm: t.Callable[[BaseModel], t.Any] | None = None, exclude_to_orm: bool = False, - exclude_from_cli: bool = False, is_attribute: bool = True, is_subscriptable: bool = False, **kwargs: t.Any, @@ -66,7 +64,7 @@ class Model(BaseModel): :param short_name: Optional short name to use for an option on a command line interface. :param option_cls: The :class:`click.Option` class to use to construct the option. :param orm_class: The class, or entry point name thereof, to which the field should be converted. If this field is - defined, the value of this field should acccept an integer which will automatically be converted to an instance + defined, the value of this field should accept an integer which will automatically be converted to an instance of said ORM class using ``orm_class.collection.get(id={field_value})``. This is useful, for example, where a field represents an instance of a different entity, such as an instance of ``User``. The serialized data would store the ``pk`` of the user, but the ORM entity instance would receive the actual ``User`` instance with that @@ -75,11 +73,14 @@ class Model(BaseModel): :param model_to_orm: Optional callable to convert the value of a field from a model instance to an ORM instance. :param exclude_to_orm: When set to ``True``, this field value will not be passed to the ORM entity constructor through ``Entity.from_model``. - :param exclude_from_cli: When set to ``True``, this field value will not be exposed on the CLI command that is - dynamically generated to create a new instance. - :param is_attribute: Whether the field is stored as an attribute. - :param is_subscriptable: Whether the field can be indexed like a list or dictionary. + :param is_attribute: Whether the field is stored as an attribute. Used by `QbFields`. + :param is_subscriptable: Whether the field can be indexed like a list or dictionary. Used by `QbFields`. """ + if exclude_to_orm: + extra = kwargs.pop('json_schema_extra', {}) + extra.update({'readOnly': True}) + kwargs['json_schema_extra'] = extra + field_info = Field(default, **kwargs) for key, value in ( @@ -90,7 +91,6 @@ class Model(BaseModel): ('orm_to_model', orm_to_model), ('model_to_orm', model_to_orm), ('exclude_to_orm', exclude_to_orm), - ('exclude_from_cli', exclude_from_cli), ('is_attribute', is_attribute), ('is_subscriptable', is_subscriptable), ): diff --git a/src/aiida/orm/__init__.py b/src/aiida/orm/__init__.py index ab98581c0a..b8dd837497 100644 --- a/src/aiida/orm/__init__.py +++ b/src/aiida/orm/__init__.py @@ -45,7 +45,6 @@ 'CifData', 'Code', 'CodeEntityLoader', - 'Collection', 'Comment', 'Computer', 'ComputerEntityLoader', @@ -53,6 +52,7 @@ 'Data', 'Dict', 'Entity', + 'EntityCollection', 'EntityExtras', 'EntityTypes', 'EnumData', diff --git a/src/aiida/orm/authinfos.py b/src/aiida/orm/authinfos.py index 8282e944fb..eba29dd850 100644 --- a/src/aiida/orm/authinfos.py +++ b/src/aiida/orm/authinfos.py @@ -10,7 +10,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Dict, Optional, Type +from typing import TYPE_CHECKING, Any, Dict, Optional, Type, cast from aiida.common import exceptions from aiida.common.pydantic import MetadataField @@ -29,11 +29,11 @@ __all__ = ('AuthInfo',) -class AuthInfoCollection(entities.Collection['AuthInfo']): +class AuthInfoCollection(entities.EntityCollection['AuthInfo']): """The collection of `AuthInfo` entries.""" @staticmethod - def _entity_base_cls() -> Type['AuthInfo']: + def _entity_base_cls() -> Type[AuthInfo]: return AuthInfo def delete(self, pk: int) -> None: @@ -44,40 +44,44 @@ def delete(self, pk: int) -> None: self._backend.authinfos.delete(pk) -class AuthInfo(entities.Entity['BackendAuthInfo', AuthInfoCollection]): +class AuthInfoModel(entities.EntityModel): + computer: int = MetadataField( + description='The PK of the computer', + is_attribute=False, + orm_class=Computer, + orm_to_model=lambda auth_info, _: cast(AuthInfo, auth_info).computer.pk, + ) + user: int = MetadataField( + description='The PK of the user', + is_attribute=False, + orm_class=User, + orm_to_model=lambda auth_info, _: cast(AuthInfo, auth_info).user.pk, + ) + enabled: bool = MetadataField( + True, + description='Whether the instance is enabled', + is_attribute=False, + ) + auth_params: Dict[str, Any] = MetadataField( + default_factory=dict, + description='Dictionary of authentication parameters', + is_attribute=False, + ) + metadata: Dict[str, Any] = MetadataField( + default_factory=dict, + description='Dictionary of metadata', + is_attribute=False, + ) + + +class AuthInfo(entities.Entity['BackendAuthInfo', AuthInfoCollection, AuthInfoModel]): """ORM class that models the authorization information that allows a `User` to connect to a `Computer`.""" + Model = AuthInfoModel + _CLS_COLLECTION = AuthInfoCollection - PROPERTY_WORKDIR = 'workdir' - class Model(entities.Entity.Model): - computer: int = MetadataField( - description='The PK of the computer', - is_attribute=False, - orm_class=Computer, - orm_to_model=lambda auth_info, _: auth_info.computer.pk, # type: ignore[attr-defined] - ) - user: int = MetadataField( - description='The PK of the user', - is_attribute=False, - orm_class=User, - orm_to_model=lambda auth_info, _: auth_info.user.pk, # type: ignore[attr-defined] - ) - enabled: bool = MetadataField( - True, - description='Whether the instance is enabled', - is_attribute=False, - ) - auth_params: Dict[str, Any] = MetadataField( - default_factory=dict, - description='Dictionary of authentication parameters', - is_attribute=False, - ) - metadata: Dict[str, Any] = MetadataField( - default_factory=dict, - description='Dictionary of metadata', - is_attribute=False, - ) + PROPERTY_WORKDIR = 'workdir' def __init__( self, diff --git a/src/aiida/orm/comments.py b/src/aiida/orm/comments.py index 8d6614de38..3311c7367c 100644 --- a/src/aiida/orm/comments.py +++ b/src/aiida/orm/comments.py @@ -8,8 +8,11 @@ ########################################################################### """Comment objects and functions""" +from __future__ import annotations + from datetime import datetime from typing import TYPE_CHECKING, List, Optional, Type, cast +from uuid import UUID from aiida.common.pydantic import MetadataField from aiida.manage import get_manager @@ -25,11 +28,11 @@ __all__ = ('Comment',) -class CommentCollection(entities.Collection['Comment']): +class CommentCollection(entities.EntityCollection['Comment']): """The collection of Comment entries.""" @staticmethod - def _entity_base_cls() -> Type['Comment']: + def _entity_base_cls() -> Type[Comment]: return Comment def delete(self, pk: int) -> None: @@ -62,34 +65,46 @@ def delete_many(self, filters: dict) -> List[int]: return self._backend.comments.delete_many(filters) -class Comment(entities.Entity['BackendComment', CommentCollection]): +class CommentModel(entities.EntityModel): + uuid: UUID = MetadataField( + description='The UUID of the comment', + is_attribute=False, + exclude_to_orm=True, + ) + ctime: datetime = MetadataField( + description='Creation time of the comment', + is_attribute=False, + exclude_to_orm=True, + ) + mtime: datetime = MetadataField( + description='Modified time of the comment', + is_attribute=False, + exclude_to_orm=True, + ) + node: int = MetadataField( + description='Node PK that the comment is attached to', + is_attribute=False, + orm_class='core.node', + orm_to_model=lambda comment, _: cast(Comment, comment).node.pk, + ) + user: int = MetadataField( + description='User PK that created the comment', + is_attribute=False, + orm_class='core.user', + orm_to_model=lambda comment, _: cast(Comment, comment).user.pk, + ) + content: str = MetadataField( + description='Content of the comment', + is_attribute=False, + ) + + +class Comment(entities.Entity['BackendComment', CommentCollection, CommentModel]): """Base class to map a DbComment that represents a comment attached to a certain Node.""" - _CLS_COLLECTION = CommentCollection + Model = CommentModel - class Model(entities.Entity.Model): - uuid: Optional[str] = MetadataField( - description='The UUID of the comment', is_attribute=False, exclude_to_orm=True - ) - ctime: Optional[datetime] = MetadataField( - description='Creation time of the comment', is_attribute=False, exclude_to_orm=True - ) - mtime: Optional[datetime] = MetadataField( - description='Modified time of the comment', is_attribute=False, exclude_to_orm=True - ) - node: int = MetadataField( - description='Node PK that the comment is attached to', - is_attribute=False, - orm_class='core.node', - orm_to_model=lambda comment, _: cast('Comment', comment).node.pk, - ) - user: int = MetadataField( - description='User PK that created the comment', - is_attribute=False, - orm_class='core.user', - orm_to_model=lambda comment, _: cast('Comment', comment).user.pk, - ) - content: str = MetadataField(description='Content of the comment', is_attribute=False) + _CLS_COLLECTION = CommentCollection def __init__( self, node: 'Node', user: 'User', content: Optional[str] = None, backend: Optional['StorageBackend'] = None diff --git a/src/aiida/orm/computers.py b/src/aiida/orm/computers.py index e8afea0b65..6e9674ba3e 100644 --- a/src/aiida/orm/computers.py +++ b/src/aiida/orm/computers.py @@ -8,8 +8,11 @@ ########################################################################### """Module for Computer entities""" +from __future__ import annotations + import os from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Type, Union +from uuid import UUID from aiida.common import exceptions from aiida.common.log import AIIDA_LOGGER, AiidaLoggerType @@ -28,14 +31,14 @@ __all__ = ('Computer',) -class ComputerCollection(entities.Collection['Computer']): +class ComputerCollection(entities.EntityCollection['Computer']): """The collection of Computer entries.""" @staticmethod - def _entity_base_cls() -> Type['Computer']: + def _entity_base_cls() -> Type[Computer]: return Computer - def get_or_create(self, label: str, **kwargs: Any) -> Tuple[bool, 'Computer']: + def get_or_create(self, label: str, **kwargs: Any) -> Tuple[bool, Computer]: """Try to retrieve a Computer from the DB with the given arguments; create (and store) a new Computer if such a Computer was not present yet. @@ -61,9 +64,45 @@ def delete(self, pk: int) -> None: return self._backend.computers.delete(pk) -class Computer(entities.Entity['BackendComputer', ComputerCollection]): +class ComputerModel(entities.EntityModel): + uuid: UUID = MetadataField( + description='The UUID of the computer', + is_attribute=False, + exclude_to_orm=True, + ) + label: str = MetadataField( + description='Label for the computer', + is_attribute=False, + ) + description: str = MetadataField( + '', + description='Description of the computer', + is_attribute=False, + ) + hostname: str = MetadataField( + description='Hostname of the computer', + is_attribute=False, + ) + transport_type: str = MetadataField( + description='Transport type of the computer', + is_attribute=False, + ) + scheduler_type: str = MetadataField( + description='Scheduler type of the computer', + is_attribute=False, + ) + metadata: Dict[str, Any] = MetadataField( + default_factory=dict, + description='Metadata of the computer', + is_attribute=False, + ) + + +class Computer(entities.Entity['BackendComputer', ComputerCollection, ComputerModel]): """Computer entity.""" + Model = ComputerModel + _logger = AIIDA_LOGGER.getChild('orm.computers') PROPERTY_MINIMUM_SCHEDULER_POLL_INTERVAL = 'minimum_scheduler_poll_interval' @@ -73,15 +112,6 @@ class Computer(entities.Entity['BackendComputer', ComputerCollection]): _CLS_COLLECTION = ComputerCollection - class Model(entities.Entity.Model): - uuid: str = MetadataField(description='The UUID of the computer', is_attribute=False, exclude_to_orm=True) - label: str = MetadataField(description='Label for the computer', is_attribute=False) - description: str = MetadataField(description='Description of the computer', is_attribute=False) - hostname: str = MetadataField(description='Hostname of the computer', is_attribute=False) - transport_type: str = MetadataField(description='Transport type of the computer', is_attribute=False) - scheduler_type: str = MetadataField(description='Scheduler type of the computer', is_attribute=False) - metadata: Dict[str, Any] = MetadataField(description='Metadata of the computer', is_attribute=False) - def __init__( self, label: Optional[str] = None, @@ -261,11 +291,11 @@ def default_memory_per_machine_validator(cls, def_memory_per_machine: Optional[i f'Invalid value for def_memory_per_machine, must be a positive int, got: {def_memory_per_machine}' ) - def copy(self) -> 'Computer': + def copy(self) -> Computer: """Return a copy of the current object to work with, not stored yet.""" return entities.from_backend_entity(Computer, self._backend_entity.copy()) - def store(self) -> 'Computer': + def store(self) -> Computer: """Store the computer in the DB. Differently from Nodes, a computer can be re-stored if its properties diff --git a/src/aiida/orm/entities.py b/src/aiida/orm/entities.py index bc9fef6554..230b884785 100644 --- a/src/aiida/orm/entities.py +++ b/src/aiida/orm/entities.py @@ -14,12 +14,11 @@ import pathlib from enum import Enum from functools import lru_cache -from typing import TYPE_CHECKING, Any, Generic, List, NoReturn, Optional, Type, TypeVar, Union +from typing import TYPE_CHECKING, Any, Generic, List, Literal, NoReturn, Optional, Type, TypeVar, Union from plumpy.base.utils import call_with_super_check, super_check -from pydantic import BaseModel -from pydantic.fields import FieldInfo -from typing_extensions import Self +from pydantic import BaseModel, ConfigDict, create_model +from typing_extensions import Self, overload from aiida.common import exceptions, log from aiida.common.exceptions import EntryPointError, InvalidOperation, NotExistent @@ -34,11 +33,12 @@ from aiida.orm.implementation import BackendEntity, StorageBackend from aiida.orm.querybuilder import FilterType, OrderByType, QueryBuilder -__all__ = ('Collection', 'Entity', 'EntityTypes') +__all__ = ('Entity', 'EntityCollection', 'EntityTypes') -CollectionType = TypeVar('CollectionType', bound='Collection[Any]') -EntityType = TypeVar('EntityType', bound='Entity[Any,Any]') BackendEntityType = TypeVar('BackendEntityType', bound='BackendEntity') +EntityCollectionType = TypeVar('EntityCollectionType', bound='EntityCollection[Any]') +EntityModelType = TypeVar('EntityModelType', bound='EntityModel') +EntityType = TypeVar('EntityType', bound='Entity[Any,Any,Any]') class EntityTypes(Enum): @@ -55,7 +55,7 @@ class EntityTypes(Enum): GROUP_NODE = 'group_node' -class Collection(abc.ABC, Generic[EntityType]): +class EntityCollection(abc.ABC, Generic[EntityType]): """Container class that represents the collection of objects of a particular entity type.""" @staticmethod @@ -147,16 +147,18 @@ def find( filters: Optional['FilterType'] = None, order_by: Optional['OrderByType'] = None, limit: Optional[int] = None, + offset: Optional[int] = None, ) -> List[EntityType]: """Find collection entries matching the filter criteria. :param filters: the keyword value pair filters to match :param order_by: a list of (key, direction) pairs specifying the sort order :param limit: the maximum number of results to return + :param offset: number of initial results to be skipped :return: a list of resulting matches """ - query = self.query(filters=filters, order_by=order_by, limit=limit) + query = self.query(filters=filters, order_by=order_by, limit=limit, offset=offset) return query.all(flat=True) def all(self) -> List[EntityType]: @@ -176,34 +178,84 @@ def count(self, filters: Optional['FilterType'] = None) -> int: return self.query(filters=filters).count() -class Entity(abc.ABC, Generic[BackendEntityType, CollectionType], metaclass=EntityFieldMeta): +class EntityModel(BaseModel, defer_build=True): + model_config = ConfigDict(extra='forbid') + + pk: int = MetadataField( + description='The primary key of the entity', + is_attribute=False, + exclude_to_orm=True, + ) + + @classmethod + def as_create_model(cls: type[EntityModel]) -> type[BaseModel]: + """Return a derived creation model class with read-only fields removed. + + This also removes any serializers/validators defined on those fields. + + :return: The derived creation model class. + """ + + # Derive the creation model from the original model + new_name = cls.__qualname__.replace('.Model', 'CreateModel') + CreateModel = create_model( # noqa: N806 + new_name, + __base__=cls, + __module__=cls.__module__, + ) + CreateModel.__qualname__ = new_name + CreateModel.model_config['extra'] = 'ignore' + + # Identify read-only fields + readonly_fields = [ + name for name, field in CreateModel.model_fields.items() if get_metadata(field, 'exclude_to_orm') + ] + + # Remove read-only fields + for name in readonly_fields: + CreateModel.model_fields.pop(name, None) + if hasattr(CreateModel, name): + delattr(CreateModel, name) + + # Prune field validators/serializers referring to read-only fields + decorators = CreateModel.__pydantic_decorators__ + + def _prune_field_decorators(field_decorators: dict[str, Any]) -> dict[str, Any]: + return { + name: decorator + for name, decorator in field_decorators.items() + if all(field not in readonly_fields for field in decorator.info.fields) + } + + decorators.field_validators = _prune_field_decorators(decorators.field_validators) + decorators.field_serializers = _prune_field_decorators(decorators.field_serializers) + + return CreateModel + + +class Entity(abc.ABC, Generic[BackendEntityType, EntityCollectionType, EntityModelType], metaclass=EntityFieldMeta): """An AiiDA entity""" - _CLS_COLLECTION: Type[CollectionType] = Collection # type: ignore[assignment] + Model: type[EntityModelType] = EntityModel # type: ignore[assignment] + + _CLS_COLLECTION: type[EntityCollectionType] = EntityCollection # type: ignore[assignment] _logger = log.AIIDA_LOGGER.getChild('orm.entities') - class Model(BaseModel, defer_build=True): - pk: Optional[int] = MetadataField( - None, - description='The primary key of the entity. Can be `None` if the entity is not yet stored.', - is_attribute=False, - exclude_to_orm=True, - exclude_from_cli=True, - ) + @classproperty + def CreateModel(cls) -> type[BaseModel]: # noqa: N802, N805 + """Return the creation version of the model class for this entity. - @classmethod - def model_to_orm_fields(cls) -> dict[str, FieldInfo]: - return { - key: field for key, field in cls.Model.model_fields.items() if not get_metadata(field, 'exclude_to_orm') - } + :return: The creation model class, with read-only fields removed. + """ + return cls.Model.as_create_model() @classmethod - def model_to_orm_field_values(cls, model: Model) -> dict[str, Any]: + def model_to_orm_field_values(cls, model: BaseModel) -> dict[str, Any]: from aiida.plugins.factories import BaseFactory fields = {} - for key, field in cls.model_to_orm_fields().items(): + for key, field in cls.CreateModel.model_fields.items(): field_value = getattr(model, key) if field_value is None: @@ -228,29 +280,114 @@ def model_to_orm_field_values(cls, model: Model) -> dict[str, Any]: return fields - def _to_model(self, repository_path: pathlib.Path) -> Model: - """Return the entity instance as an instance of its model.""" - fields = {} + def orm_to_model_field_values( + self, + *, + repository_path: Optional[pathlib.Path] = None, + serialize_repository_content: bool = False, + skip_read_only: bool = False, + ) -> dict[str, Any]: + """Collect values for the ``Model``'s fields from this entity. + + Centralizes mapping of ORM -> Model values, including handling of ``orm_to_model`` functions + and optional filtering based on field metadata (e.g., excluding CLI-only fields). + + :param repository_path: Optional path to use for repository-based fields. + :param serialize_repository_content: Whether to include repository file content. + :param skip_read_only: When True, fields marked with ``exclude_to_orm`` are skipped. + :return: Mapping of field name to value. + """ + fields: dict[str, Any] = {} + + Model = self.Model if self.is_stored else self.CreateModel # noqa: N806 + + for key, field in Model.model_fields.items(): + if skip_read_only and get_metadata(field, 'exclude_to_orm'): + continue - for key, field in self.Model.model_fields.items(): if orm_to_model := get_metadata(field, 'orm_to_model'): - fields[key] = orm_to_model(self, repository_path) + kwargs = { + 'serialize_repository_content': serialize_repository_content, # see `Node` model + 'repository_path': repository_path, # see `PortableCode` model + } + fields[key] = orm_to_model(self, kwargs) else: fields[key] = getattr(self, key) - return self.Model(**fields) + return fields + + @overload + def to_model( + self, + *, + repository_path: Optional[pathlib.Path] = None, + serialize_repository_content: bool = False, + ) -> EntityModelType: ... + + @overload + def to_model( + self, + *, + repository_path: Optional[pathlib.Path] = None, + serialize_repository_content: bool = False, + ) -> BaseModel: ... + + @overload + def to_model( + self, + *, + repository_path: Optional[pathlib.Path] = None, + serialize_repository_content: bool = False, + ) -> BaseModel: ... + + def to_model( + self, + *, + repository_path: Optional[pathlib.Path] = None, + serialize_repository_content: bool = False, + ) -> BaseModel: + """Return the entity instance as an instance of its model. + + :param repository_path: If the orm node has files in the repository, this path is used to read the repository + files from. If no path is specified a temporary path is created using the entities pk. + :param serialize_repository_content: If True, repository file content is serialized in the model. + This field can be very large, so it is excluded by default. + :return: An instance of the entity's model class. + """ + fields = self.orm_to_model_field_values( + repository_path=repository_path, + serialize_repository_content=serialize_repository_content, + ) + Model = self.Model if self.is_stored else self.CreateModel # noqa: N806 + return Model(**fields) @classmethod - def _from_model(cls, model: Model) -> Self: - """Return an entity instance from an instance of its model.""" + def from_model(cls, model: BaseModel) -> Self: + """Return an entity instance from an instance of its model. + + :param model: An instance of the entity's model class. + :return: An instance of the entity class. + """ fields = cls.model_to_orm_field_values(model) return cls(**fields) - def serialize(self, repository_path: Union[pathlib.Path, None] = None) -> dict[str, Any]: + def serialize( + self, + *, + repository_path: Optional[pathlib.Path] = None, + serialize_repository_content: bool = False, + mode: Literal['json', 'python'] = 'json', + ) -> dict[str, Any]: """Serialize the entity instance to JSON. - :param repository_path: If the orm node has files in the repository, this path is used to dump the repostiory + :param repository_path: If the orm node has files in the repository, this path is used to dump the repository files to. If no path is specified a temporary path is created using the entities pk. + :param serialize_repository_content: If True, repository file content is serialized in the model. + This field can be very large, so it is excluded by default. + :param mode: The serialization mode, either 'json' or 'python'. The 'json' mode is the most strict and ensures + that the output is JSON serializable, whereas the 'python' mode allows for more complex Python types, such + as `datetime` objects. + :return: A dictionary that can be serialized to JSON. """ self.logger.warning( 'Serialization through pydantic is still an experimental feature and might break in future releases.' @@ -265,18 +402,25 @@ def serialize(self, repository_path: Union[pathlib.Path, None] = None) -> dict[s raise ValueError(f'The repository_path `{repository_path}` does not exist.') if not repository_path.is_dir(): raise ValueError(f'The repository_path `{repository_path}` is not a directory.') - return self._to_model(repository_path).model_dump() + return self.to_model( + repository_path=repository_path, + serialize_repository_content=serialize_repository_content, + ).model_dump(mode=mode) @classmethod - def from_serialized(cls, **kwargs: dict[str, Any]) -> Self: - """Construct an entity instance from JSON serialized data.""" + def from_serialized(cls, serialized: dict[str, Any]) -> Self: + """Construct an entity instance from JSON serialized data. + + :param serialized: A dictionary representing the serialized entity. + :return: An instance of the entity class. + """ cls._logger.warning( 'Serialization through pydantic is still an experimental feature and might break in future releases.' ) - return cls._from_model(cls.Model(**kwargs)) + return cls.from_model(cls.CreateModel(**serialized)) @classproperty - def objects(cls: EntityType) -> CollectionType: # noqa: N805 + def objects(cls: EntityType) -> EntityCollectionType: # noqa: N805 """Get a collection for objects of this type, with the default backend. .. deprecated:: This will be removed in v3, use ``collection`` instead. @@ -287,7 +431,7 @@ def objects(cls: EntityType) -> CollectionType: # noqa: N805 return cls.collection @classproperty - def collection(cls) -> CollectionType: # noqa: N805 + def collection(cls) -> EntityCollectionType: # noqa: N805 """Get a collection for objects of this type, with the default backend. :return: an object that can be used to access entities of this type @@ -295,7 +439,7 @@ def collection(cls) -> CollectionType: # noqa: N805 return cls._CLS_COLLECTION.get_cached(cls, get_manager().get_profile_storage()) @classmethod - def get_collection(cls, backend: 'StorageBackend') -> CollectionType: + def get_collection(cls, backend: 'StorageBackend') -> EntityCollectionType: """Get a collection for objects of this type for a given backend. .. note:: Use the ``collection`` class property instead if the currently loaded backend or backend of the diff --git a/src/aiida/orm/fields.py b/src/aiida/orm/fields.py index f0e8ca390b..68619b0197 100644 --- a/src/aiida/orm/fields.py +++ b/src/aiida/orm/fields.py @@ -354,6 +354,10 @@ class QbFields: def __init__(self, fields: t.Optional[t.Dict[str, QbField]] = None): self._fields = fields or {} + def keys(self) -> list[str]: + """Return the field keys, prefixed with 'attribute.' if field is an attribute.""" + return [field.backend_key for field in self._fields.values()] + def __repr__(self) -> str: return pformat({key: str(value) for key, value in self._fields.items()}) @@ -457,14 +461,14 @@ def __init__(cls, name, bases, classdict): if model_bases != cls_model_bases and not getattr(cls, '_SKIP_MODEL_INHERITANCE_CHECK', False): bases = [f'{e.__module__}.{e.__name__}.Model' for e in cls_bases_with_model_leaves] raise RuntimeError( - f'`{cls.__name__}.Model` does not subclass all necessary base classes. It should be: ' - f'`class Model({", ".join(sorted(bases))}):`' + f'`{cls.__name__}Model` does not subclass all necessary base classes. It should be: ' + f'`class {cls.__name__}Model({", ".join(sorted(bases))}):`' ) for key, field in cls.Model.model_fields.items(): fields[key] = add_field( key, - alias=get_metadata(field, 'alias', None), + alias=field.alias, dtype=field.annotation, doc=field.description, is_attribute=get_metadata(field, 'is_attribute', False), diff --git a/src/aiida/orm/groups.py b/src/aiida/orm/groups.py index 8582ee56b9..d8bfdec63b 100644 --- a/src/aiida/orm/groups.py +++ b/src/aiida/orm/groups.py @@ -6,13 +6,16 @@ # For further information on the license, see the LICENSE.txt file # # For further information please visit http://www.aiida.net # ########################################################################### -"""AiiDA Group entites""" +"""AiiDA Group entities""" + +from __future__ import annotations import datetime import warnings from functools import cached_property from pathlib import Path from typing import TYPE_CHECKING, Any, ClassVar, Dict, Optional, Sequence, Tuple, Type, Union, cast +from uuid import UUID from typing_extensions import Self @@ -34,7 +37,7 @@ __all__ = ('AutoGroup', 'Group', 'ImportGroup', 'UpfFamily') -def load_group_class(type_string: str) -> Type['Group']: +def load_group_class(type_string: str) -> Type[Group]: """Load the sub class of `Group` that corresponds to the given `type_string`. .. note:: will fall back on `aiida.orm.groups.Group` if `type_string` cannot be resolved to loadable entry point. @@ -55,14 +58,14 @@ def load_group_class(type_string: str) -> Type['Group']: return group_class -class GroupCollection(entities.Collection['Group']): +class GroupCollection(entities.EntityCollection['Group']): """Collection of Groups""" @staticmethod - def _entity_base_cls() -> Type['Group']: + def _entity_base_cls() -> Type[Group]: return Group - def get_or_create(self, label: Optional[str] = None, **kwargs) -> Tuple['Group', bool]: + def get_or_create(self, label: Optional[str] = None, **kwargs) -> Tuple[Group, bool]: """Try to retrieve a group from the DB with the given arguments; create (and store) a new group if such a group was not present yet. @@ -95,9 +98,9 @@ def delete(self, pk: int) -> None: class GroupBase: """A namespace for group related functionality, that is not directly related to its user-facing properties.""" - def __init__(self, group: 'Group') -> None: + def __init__(self, group: Group) -> None: """Construct a new instance of the base namespace.""" - self._group: 'Group' = group + self._group: Group = group @cached_property def extras(self) -> extras.EntityExtras: @@ -105,31 +108,53 @@ def extras(self) -> extras.EntityExtras: return extras.EntityExtras(self._group) -class Group(entities.Entity['BackendGroup', GroupCollection]): +class GroupModel(entities.EntityModel): + uuid: UUID = MetadataField( + description='The UUID of the group', + is_attribute=False, + exclude_to_orm=True, + ) + type_string: str = MetadataField( + description='The type of the group', + is_attribute=False, + exclude_to_orm=True, + ) + user: int = MetadataField( + description='The PK of the group owner', + is_attribute=False, + orm_class='core.user', + orm_to_model=lambda group, _: cast(Group, group).user.pk, + exclude_to_orm=True, + ) + time: datetime.datetime = MetadataField( + description='The creation time of the node, defaults to now (timezone-aware)', + is_attribute=False, + exclude_to_orm=True, + ) + label: str = MetadataField( + description='The group label', + is_attribute=False, + ) + description: str = MetadataField( + '', + description='The group description', + is_attribute=False, + ) + extras: Dict[str, Any] = MetadataField( + default_factory=dict, + description='The group extras', + is_attribute=False, + is_subscriptable=True, + orm_to_model=lambda group, _: cast(Group, group).base.extras.all, + ) + + +class Group(entities.Entity['BackendGroup', GroupCollection, GroupModel]): """An AiiDA ORM implementation of group of nodes.""" - __type_string: ClassVar[Optional[str]] + Model = GroupModel - class Model(entities.Entity.Model): - uuid: str = MetadataField(description='The UUID of the group', is_attribute=False, exclude_to_orm=True) - type_string: str = MetadataField(description='The type of the group', is_attribute=False, exclude_to_orm=True) - user: int = MetadataField( - description='The group owner', - is_attribute=False, - orm_class='core.user', - orm_to_model=lambda group, _: group.user.pk, # type: ignore[attr-defined] - ) - time: Optional[datetime.datetime] = MetadataField( - description='The creation time of the node', is_attribute=False - ) - label: str = MetadataField(description='The group label', is_attribute=False) - description: Optional[str] = MetadataField(description='The group description', is_attribute=False) - extras: Optional[Dict[str, Any]] = MetadataField( - description='The group extras', - is_attribute=False, - is_subscriptable=True, - orm_to_model=lambda group, _: group.base.extras.all, # type: ignore[attr-defined] - ) + __type_string: ClassVar[Optional[str]] _CLS_COLLECTION = GroupCollection diff --git a/src/aiida/orm/logs.py b/src/aiida/orm/logs.py index ed07deb847..b5e19adf48 100644 --- a/src/aiida/orm/logs.py +++ b/src/aiida/orm/logs.py @@ -8,9 +8,12 @@ ########################################################################### """Module for orm logging abstract classes""" +from __future__ import annotations + import logging from datetime import datetime from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type +from uuid import UUID from aiida.common import timezone from aiida.common.pydantic import MetadataField @@ -34,16 +37,16 @@ def OrderSpecifier(field, direction): # noqa: N802 return {field: direction} -class LogCollection(entities.Collection['Log']): +class LogCollection(entities.EntityCollection['Log']): """This class represents the collection of logs and can be used to create and retrieve logs. """ @staticmethod - def _entity_base_cls() -> Type['Log']: + def _entity_base_cls() -> Type[Log]: return Log - def create_entry_from_record(self, record: logging.LogRecord) -> Optional['Log']: + def create_entry_from_record(self, record: logging.LogRecord) -> Optional[Log]: """Helper function to create a log entry from a record created as by the python logging library :param record: The record created by the logging module @@ -81,7 +84,7 @@ def create_entry_from_record(self, record: logging.LogRecord) -> Optional['Log'] backend=self.backend, ) - def get_logs_for(self, entity: 'Node', order_by: Optional['OrderByType'] = None) -> List['Log']: + def get_logs_for(self, entity: 'Node', order_by: Optional['OrderByType'] = None) -> List[Log]: """Get all the log messages for a given node and optionally sort :param entity: the entity to get logs for @@ -124,19 +127,45 @@ def delete_many(self, filters: 'FilterType') -> List[int]: return self._backend.logs.delete_many(filters) -class Log(entities.Entity['BackendLog', LogCollection]): +class LogModel(entities.EntityModel): + uuid: UUID = MetadataField( + description='The UUID of the node', + is_attribute=False, + exclude_to_orm=True, + ) + loggername: str = MetadataField( + description='The name of the logger', + is_attribute=False, + ) + levelname: str = MetadataField( + description='The name of the log level', + is_attribute=False, + ) + message: str = MetadataField( + description='The message of the log', + is_attribute=False, + ) + time: datetime = MetadataField( + description='The time at which the log was created', + is_attribute=False, + ) + metadata: Dict[str, Any] = MetadataField( + default_factory=dict, + description='The metadata of the log', + is_attribute=False, + ) + dbnode_id: int = MetadataField( + description='Associated node', + is_attribute=False, + ) + + +class Log(entities.Entity['BackendLog', LogCollection, LogModel]): """An AiiDA Log entity. Corresponds to a logged message against a particular AiiDA node.""" - _CLS_COLLECTION = LogCollection + Model = LogModel - class Model(entities.Entity.Model): - uuid: str = MetadataField(description='The UUID of the node', is_attribute=False, exclude_to_orm=True) - loggername: str = MetadataField(description='The name of the logger', is_attribute=False) - levelname: str = MetadataField(description='The name of the log level', is_attribute=False) - message: str = MetadataField(description='The message of the log', is_attribute=False) - time: datetime = MetadataField(description='The time at which the log was created', is_attribute=False) - metadata: Dict[str, Any] = MetadataField(description='The metadata of the log', is_attribute=False) - dbnode_id: int = MetadataField(description='Associated node', is_attribute=False) + _CLS_COLLECTION = LogCollection def __init__( self, diff --git a/src/aiida/orm/nodes/__init__.py b/src/aiida/orm/nodes/__init__.py index e8ef1da246..4a21214d3c 100644 --- a/src/aiida/orm/nodes/__init__.py +++ b/src/aiida/orm/nodes/__init__.py @@ -44,6 +44,7 @@ 'Node', 'NodeAttributes', 'NodeRepository', + 'NodeType', 'NumericType', 'OrbitalData', 'PortableCode', diff --git a/src/aiida/orm/nodes/data/array/array.py b/src/aiida/orm/nodes/data/array/array.py index 9118187b0c..b6cfdd7482 100644 --- a/src/aiida/orm/nodes/data/array/array.py +++ b/src/aiida/orm/nodes/data/array/array.py @@ -12,15 +12,16 @@ import base64 import io -from typing import Any, Iterator, Optional +from collections.abc import Iterable +from typing import Any, Iterator, Optional, Union, cast import numpy as np -from pydantic import ConfigDict +from pydantic import field_validator from aiida.common.pydantic import MetadataField +from .. import data from ..base import to_aiida_type -from ..data import Data __all__ = ('ArrayData',) @@ -30,7 +31,33 @@ def _(value): return ArrayData(value) -class ArrayData(Data): +class ArrayDataModel(data.DataModel): + arrays: Optional[dict[str, bytes]] = MetadataField( + None, + description='The dictionary of numpy arrays.', + orm_to_model=lambda node, _: ArrayData.save_arrays(cast(ArrayData, node).arrays), + model_to_orm=lambda model: ArrayData.load_arrays(cast(ArrayDataModel, model).arrays), + ) + + @field_validator('arrays', mode='before') + @classmethod + def validate_arrays(cls, value: Optional[dict[str, Union[bytes, Any]]]) -> Any: + if value is None: + return value + if not isinstance(value, dict): + raise TypeError(f'`arrays` should be a dictionary but got: {value}') + arrays: dict[str, bytes] = {} + for key, array in value.items(): + if isinstance(array, bytes): + arrays[key] = array + elif isinstance(array, Iterable): + arrays |= ArrayData.save_arrays({key: np.array(array)}) + else: + arrays[key] = array + return arrays + + +class ArrayData(data.Data): """Store a set of arrays on disk (rather than on the database) in an efficient way Arrays are stored using numpy and therefore this class requires numpy to be installed. @@ -47,14 +74,7 @@ class ArrayData(Data): """ - class Model(Data.Model): - model_config = ConfigDict(arbitrary_types_allowed=True) - arrays: Optional[dict[str, bytes]] = MetadataField( - None, - description='The dictionary of numpy arrays.', - orm_to_model=lambda node, _: ArrayData.save_arrays(node.arrays), # type: ignore[attr-defined] - model_to_orm=lambda model: ArrayData.load_arrays(model.arrays), # type: ignore[attr-defined] - ) + Model = ArrayDataModel array_prefix = 'array|' default_array_name = 'default' @@ -83,7 +103,7 @@ def __init__(self, arrays: np.ndarray | dict[str, np.ndarray] | None = None, **k @staticmethod def save_arrays(arrays: dict[str, np.ndarray]) -> dict[str, bytes]: - results = {} + results: dict[str, bytes] = {} for key, array in arrays.items(): stream = io.BytesIO() @@ -94,8 +114,11 @@ def save_arrays(arrays: dict[str, np.ndarray]) -> dict[str, bytes]: return results @staticmethod - def load_arrays(arrays: dict[str, bytes]) -> dict[str, np.ndarray]: - results = {} + def load_arrays(arrays: dict[str, bytes] | None) -> dict[str, np.ndarray]: + results: dict[str, np.ndarray] = {} + + if arrays is None: + return results for key, encoded in arrays.items(): stream = io.BytesIO(base64.decodebytes(encoded)) @@ -227,8 +250,7 @@ def set_array(self, name: str, array: np.ndarray) -> None: # Check if the name is valid if not name or re.sub('[0-9a-zA-Z_]', '', name): raise ValueError( - 'The name assigned to the array ({}) is not valid,' - 'it can only contain digits, letters and underscores' + 'The name assigned to the array ({}) is not valid,it can only contain digits, letters and underscores' ) # Write the array to a temporary file, and then add it to the repository of the node diff --git a/src/aiida/orm/nodes/data/array/bands.py b/src/aiida/orm/nodes/data/array/bands.py index f104d9222e..fc755be40b 100644 --- a/src/aiida/orm/nodes/data/array/bands.py +++ b/src/aiida/orm/nodes/data/array/bands.py @@ -10,6 +10,8 @@ in a Brillouin zone, and how to operate on them. """ +from __future__ import annotations + import json import typing as t from string import Template @@ -20,7 +22,7 @@ from aiida.common.pydantic import MetadataField from aiida.common.utils import join_labels, prettify_labels -from .kpoints import KpointsData +from . import kpoints __all__ = ('BandsData', 'find_bandgap') @@ -141,7 +143,7 @@ def nint(num): lumo = [_[0][_[1] + 1] for _ in zip(bands, homo_indexes)] except IndexError: raise ValueError( - 'To understand if it is a metal or insulator, ' 'need more bands than n_band=number_electrons' + 'To understand if it is a metal or insulator, need more bands than n_band=number_electrons' ) else: @@ -158,7 +160,7 @@ def nint(num): lumo = [i[number_electrons // number_electrons_per_band] for i in bands] # take the n+1th level except IndexError: raise ValueError( - 'To understand if it is a metal or insulator, ' 'need more bands than n_band=number_electrons' + 'To understand if it is a metal or insulator, need more bands than n_band=number_electrons' ) if number_electrons % 2 == 1 and len(stored_bands.shape) == 2: @@ -210,18 +212,38 @@ def nint(num): return True, gap -class BandsData(KpointsData): +class BandsDataModel(kpoints.KpointsDataModel): + array_labels: t.Optional[t.List[str]] = MetadataField( + None, + description='Labels associated with the band arrays', + ) + units: t.Optional[str] = MetadataField( + None, + description='Units in which the data in bands were stored', + orm_to_model=lambda node, _: t.cast(BandsData, node).base.attributes.get('units', None), + ) + + +class BandsData(kpoints.KpointsData): """Class to handle bands data""" - class Model(KpointsData.Model): - array_labels: t.Optional[t.List[str]] = MetadataField(description='Labels associated with the band arrays') - units: str = MetadataField(description='Units in which the data in bands were stored') + Model = BandsDataModel + + def __init__( + self, + *, + array_labels: list[str] | None = None, + units: str | None = None, + **kwargs, + ): + super().__init__(**kwargs) + self.units = units def set_kpointsdata(self, kpointsdata): """Load the kpoints from a kpoint object. :param kpointsdata: an instance of KpointsData class """ - if not isinstance(kpointsdata, KpointsData): + if not isinstance(kpointsdata, kpoints.KpointsData): raise ValueError('kpointsdata must be of the KpointsData class') try: self.cell = kpointsdata.cell @@ -304,7 +326,7 @@ def _validate_bands_occupations(self, bands, occupations=None, labels=None): the_labels = [str(_) for _ in labels] else: raise ValidationError( - 'Band labels have an unrecognized type ({})' 'but should be a string or a list of strings'.format( + 'Band labels have an unrecognized type ({}) but should be a string or a list of strings'.format( labels.__class__ ) ) diff --git a/src/aiida/orm/nodes/data/array/kpoints.py b/src/aiida/orm/nodes/data/array/kpoints.py index e7958970a4..f9081cd2fe 100644 --- a/src/aiida/orm/nodes/data/array/kpoints.py +++ b/src/aiida/orm/nodes/data/array/kpoints.py @@ -11,13 +11,15 @@ periodic crystal structure). """ +from __future__ import annotations + import typing as t import numpy from aiida.common.pydantic import MetadataField -from .array import ArrayData +from . import array __all__ = ('KpointsData',) @@ -25,7 +27,50 @@ _DEFAULT_EPSILON_ANGLE = 1e-5 -class KpointsData(ArrayData): +class KpointsDataModel(array.ArrayDataModel): + labels: t.Optional[t.List[str]] = MetadataField( + None, + description='Labels associated with the list of kpoints', + orm_to_model=lambda node, _: t.cast(KpointsData, node).base.attributes.get('labels', None), + ) + label_numbers: t.Optional[t.List[int]] = MetadataField( + None, + description='Index of the labels in the list of kpoints', + orm_to_model=lambda node, _: t.cast(KpointsData, node).base.attributes.get('label_numbers', None), + ) + cell: t.Optional[t.List[t.List[float]]] = MetadataField( + None, + description='Unit cell of the crystal, in Angstroms', + orm_to_model=lambda node, _: t.cast(KpointsData, node).base.attributes.get('cell', None), + ) + pbc1: t.Optional[bool] = MetadataField( + None, + description='Periodicity in the first lattice vector direction', + orm_to_model=lambda node, _: t.cast(KpointsData, node).pbc[0], + ) + pbc2: t.Optional[bool] = MetadataField( + None, + description='Periodicity in the second lattice vector direction', + orm_to_model=lambda node, _: t.cast(KpointsData, node).pbc[1], + ) + pbc3: t.Optional[bool] = MetadataField( + None, + description='Periodicity in the third lattice vector direction', + orm_to_model=lambda node, _: t.cast(KpointsData, node).pbc[2], + ) + mesh: t.Optional[t.List[int]] = MetadataField( + None, + description='Mesh of kpoints', + orm_to_model=lambda node, _: t.cast(KpointsData, node).base.attributes.get('mesh', None), + ) + offset: t.Optional[t.List[float]] = MetadataField( + None, + description='Offset of kpoints', + orm_to_model=lambda node, _: t.cast(KpointsData, node).base.attributes.get('offset', None), + ) + + +class KpointsData(array.ArrayData): """Class to handle array of kpoints in the Brillouin zone. Provide methods to generate either user-defined k-points or path of k-points along symmetry lines. @@ -38,15 +83,60 @@ class KpointsData(ArrayData): set_cell_from_structure methods. """ - class Model(ArrayData.Model): - labels: t.List[str] = MetadataField(description='Labels associated with the list of kpoints') - label_numbers: t.List[int] = MetadataField(description='Index of the labels in the list of kpoints') - mesh: t.List[int] = MetadataField(description='Mesh of kpoints') - offset: t.List[float] = MetadataField(description='Offset of kpoints') - cell: t.List[t.List[float]] = MetadataField(description='Unit cell of the crystal, in Angstroms') - pbc1: bool = MetadataField(description='True if the first lattice vector is periodic') - pbc2: bool = MetadataField(description='True if the second lattice vector is periodic') - pbc3: bool = MetadataField(description='True if the third lattice vector is periodic') + Model = KpointsDataModel + + def __init__( + self, + *, + labels: list[str] | None = None, + label_numbers: list[int] | None = None, + cell: list[list[float]] | None = None, + pbc1: bool | None = None, + pbc2: bool | None = None, + pbc3: bool | None = None, + mesh: list[int] | None = None, + offset: list[float] | None = None, + **kwargs, + ): + arrays = kwargs.pop('arrays', None) + + super().__init__(**kwargs) + + if offset is not None and mesh is None: + raise ValueError('Cannot set an offset without a kpoints mesh') + + given_list_kwargs = any(kwarg is not None for kwarg in (labels, label_numbers, cell, pbc1, pbc2, pbc3)) + + if mesh is not None: + if arrays is not None or given_list_kwargs: + raise ValueError('When providing a kpoints mesh, only mesh and offset are allowed') + self.set_kpoints_mesh(mesh, offset=offset) + + if arrays is not None: + if labels is not None and label_numbers is not None: + if len(labels) != len(label_numbers): + raise ValueError('Labels and label numbers must have the same length') + label_list = list(zip(label_numbers, labels)) + else: + label_list = None + + pbc = (pbc1 or False, pbc2 or False, pbc3 or False) + + if cell is not None: + self.set_cell(cell, pbc) + else: + self.pbc = pbc + + if isinstance(arrays, dict): + kpoints = arrays.get('kpoints', None) + if kpoints is None: + raise ValueError("When providing a dict of arrays, it must contain the key 'kpoints'") + arrays = kpoints + + self.set_kpoints(arrays, labels=label_list) + + elif given_list_kwargs: + raise ValueError('Missing kpoints list') def get_description(self): """Returns a string with infos retrieved from kpoints node's properties. @@ -99,8 +189,11 @@ def pbc(self): :return: a tuple of three booleans, each one tells if there are periodic boundary conditions for the i-th real-space direction (i=1,2,3) """ - # return copy.deepcopy(self._pbc) - return (self.base.attributes.get('pbc1'), self.base.attributes.get('pbc2'), self.base.attributes.get('pbc3')) + return ( + self.base.attributes.get('pbc1', False), + self.base.attributes.get('pbc2', False), + self.base.attributes.get('pbc3', False), + ) @pbc.setter def pbc(self, value): @@ -194,7 +287,7 @@ def set_cell_from_structure(self, structuredata): if not isinstance(structuredata, StructureData): raise ValueError( - 'An instance of StructureData should be passed to ' 'the KpointsData, found instead {}'.format( + 'An instance of StructureData should be passed to the KpointsData, found instead {}'.format( structuredata.__class__ ) ) @@ -333,12 +426,8 @@ def _dimension(self): """Dimensionality of the structure, found from its pbc (i.e. 1 if it's a 1D structure, 2 if its 2D, 3 if it's 3D ...). :return dimensionality: 0, 1, 2 or 3 - :note: will return 3 if pbc has not been set beforehand """ - try: - return sum(self.pbc) - except AttributeError: - return 3 + return sum(self.pbc) def _validate_kpoints_weights(self, kpoints, weights): """Validate the list of kpoints and of weights before storage. @@ -355,9 +444,9 @@ def _validate_kpoints_weights(self, kpoints, weights): kpoints = numpy.array([[0.0, 0.0, 0.0]]) else: raise ValueError( - 'empty kpoints list is valid only in zero dimension' - '; instead here with have {} dimensions' - ''.format(self._dimension) + 'empty kpoints list is valid only in zero dimension; instead here with have {} dimensions'.format( + self._dimension + ) ) if len(kpoints.shape) <= 1: @@ -373,8 +462,9 @@ def _validate_kpoints_weights(self, kpoints, weights): if kpoints.shape[1] < self._dimension: raise ValueError( - 'In a system which has {0} dimensions, kpoint need' - 'more than {0} coordinates (found instead {1})'.format(self._dimension, kpoints.shape[1]) + 'In a system with {0} dimensions, kpoints need more than {0} coordinates (found instead {1})'.format( + self._dimension, kpoints.shape[1] + ) ) if weights is not None: diff --git a/src/aiida/orm/nodes/data/array/trajectory.py b/src/aiida/orm/nodes/data/array/trajectory.py index 3d6356ebd2..05c1c8be76 100644 --- a/src/aiida/orm/nodes/data/array/trajectory.py +++ b/src/aiida/orm/nodes/data/array/trajectory.py @@ -8,30 +8,70 @@ ########################################################################### """AiiDA class to deal with crystal structure trajectories.""" +from __future__ import annotations + import collections.abc -from typing import List +from typing import TYPE_CHECKING, List, Optional + +import numpy as np from aiida.common.pydantic import MetadataField -from .array import ArrayData +from . import array + +if TYPE_CHECKING: + from aiida import orm + +if TYPE_CHECKING: + from aiida import orm __all__ = ('TrajectoryData',) -class TrajectoryData(ArrayData): +class TrajectoryDataModel(array.ArrayDataModel): + units_positions: Optional[str] = MetadataField( + None, + serialization_alias='units|positions', + description='Unit of positions', + ) + units_times: Optional[str] = MetadataField( + None, + serialization_alias='units|times', + description='Unit of time', + ) + symbols: List[str] = MetadataField(description='List of symbols') + + +class TrajectoryData(array.ArrayData): """Stores a trajectory (a sequence of crystal structures with timestamps, and possibly with velocities). """ - class Model(ArrayData.Model): - units_positions: str = MetadataField(alias='units|positions', description='Unit of positions') - units_times: str = MetadataField(alias='units|times', description='Unit of time') - symbols: List[str] = MetadataField(description='List of symbols') - - def __init__(self, structurelist=None, **kwargs): + Model = TrajectoryDataModel + + def __init__( + self, + structurelist: list[orm.StructureData] | None = None, + units_positions: str | None = None, + units_times: str | None = None, + symbols: list[str] | None = None, + arrays: np.ndarray | dict[str, np.ndarray] | None = None, + **kwargs, + ): super().__init__(**kwargs) + self.unit_positions = units_positions + self.unit_times = units_times if structurelist is not None: self.set_structurelist(structurelist) + elif arrays is not None: + self.set_trajectory( + symbols=symbols, + positions=arrays['positions'], + stepids=arrays.get('steps'), + cells=arrays.get('cells'), + times=arrays.get('times'), + velocities=arrays.get('velocities'), + ) def _internal_validate(self, stepids, cells, symbols, positions, times, velocities): """Internal function to validate the type and shape of the arrays. See @@ -70,7 +110,7 @@ def _internal_validate(self, stepids, cells, symbols, positions, times, velociti numatoms = len(symbols) if positions.shape != (numsteps, numatoms, 3): raise ValueError( - 'TrajectoryData.positions must have shape (s,n,3), ' 'with s=number of steps and n=number of symbols' + 'TrajectoryData.positions must have shape (s,n,3), with s=number of steps and n=number of symbols' ) if times is not None: if times.shape != (numsteps,): @@ -256,6 +296,26 @@ def get_cells(self): except (AttributeError, KeyError): return None + @property + def units_positions(self) -> str | None: + """Units for the positions array.""" + return self.base.attributes.get('units|positions', None) + + @units_positions.setter + def units_positions(self, units: str) -> None: + """Set units for the positions array.""" + self.base.attributes.set('units|positions', units) + + @property + def units_times(self) -> str | None: + """Units for the times array.""" + return self.base.attributes.get('units|times', None) + + @units_times.setter + def units_times(self, units: str) -> None: + """Set units for the times array.""" + self.base.attributes.set('units|times', units) + @property def symbols(self) -> List[str]: """Return the array of symbols, if it has already been set. @@ -371,7 +431,7 @@ def get_step_structure(self, index, custom_kinds=None): for k in custom_kinds: if not isinstance(k, Kind): raise TypeError( - 'Each element of the custom_kinds list must ' 'be a aiida.orm.nodes.data.structure.Kind object' + 'Each element of the custom_kinds list must be a aiida.orm.nodes.data.structure.Kind object' ) kind_names.append(k.name) if len(kind_names) != len(set(kind_names)): diff --git a/src/aiida/orm/nodes/data/base.py b/src/aiida/orm/nodes/data/base.py index adc0f3a98e..937f6876a9 100644 --- a/src/aiida/orm/nodes/data/base.py +++ b/src/aiida/orm/nodes/data/base.py @@ -13,7 +13,7 @@ from aiida.common.pydantic import MetadataField -from .data import Data +from . import data __all__ = ('BaseType', 'to_aiida_type') @@ -24,15 +24,18 @@ def to_aiida_type(value): raise TypeError(f'Cannot convert value of type {type(value)} to AiiDA type.') -class BaseType(Data): +class BaseTypeModel(data.DataModel): + value: t.Any = MetadataField( + ..., + title='Data value', + description='The value of the data', + ) + + +class BaseType(data.Data): """`Data` sub class to be used as a base for data containers that represent base python data types.""" - class Model(Data.Model): - value: t.Any = MetadataField( - ..., - title='Data value.', - description='The value of the data', - ) + Model = BaseTypeModel def __init__(self, value=None, **kwargs): try: diff --git a/src/aiida/orm/nodes/data/bool.py b/src/aiida/orm/nodes/data/bool.py index 61c8e61d5e..06a615a3e9 100644 --- a/src/aiida/orm/nodes/data/bool.py +++ b/src/aiida/orm/nodes/data/bool.py @@ -10,14 +10,26 @@ import numpy -from .base import BaseType, to_aiida_type +from aiida.common.pydantic import MetadataField + +from . import base +from .base import to_aiida_type __all__ = ('Bool',) -class Bool(BaseType): +class BoolModel(base.BaseTypeModel): + value: bool = MetadataField( + title='Boolean value', + description='The value of the boolean', + ) + + +class Bool(base.BaseType): """`Data` sub class to represent a boolean value.""" + Model = BoolModel + _type = bool def __int__(self): diff --git a/src/aiida/orm/nodes/data/cif.py b/src/aiida/orm/nodes/data/cif.py index 8421a617eb..4d514a8d05 100644 --- a/src/aiida/orm/nodes/data/cif.py +++ b/src/aiida/orm/nodes/data/cif.py @@ -9,12 +9,12 @@ """Tools for handling Crystallographic Information Files (CIF)""" import re -import typing as t +from typing import List, Optional from aiida.common.pydantic import MetadataField from aiida.common.utils import Capturing -from .singlefile import SinglefileData +from . import singlefile __all__ = ('CifData', 'cif_from_ase', 'has_pycifrw', 'pycifrw_from_cif') @@ -232,7 +232,25 @@ def chemcount_str_to_number(string): # Note: Method 'query' is abstract in class 'Node' but is not overridden -class CifData(SinglefileData): +class CifDataModel(singlefile.SinglefileDataModel): + formulae: Optional[List[str]] = MetadataField( + None, + description='List of formulae contained in the CIF file.', + exclude_to_orm=True, + ) + spacegroup_numbers: Optional[List[str]] = MetadataField( + None, + description='List of space group numbers of the structure.', + exclude_to_orm=True, + ) + md5: Optional[str] = MetadataField( + None, + description='MD5 checksum of the file contents.', + exclude_to_orm=True, + ) + + +class CifData(singlefile.SinglefileData): """Wrapper for Crystallographic Interchange File (CIF) .. note:: the file (physical) is held as the authoritative source of @@ -241,6 +259,8 @@ class CifData(SinglefileData): first, the values are updated from the physical CIF file. """ + Model = CifDataModel + _SET_INCOMPATIBILITIES = [('ase', 'file'), ('ase', 'values'), ('file', 'values')] _SCAN_TYPES = ('standard', 'flex') _SCAN_TYPE_DEFAULT = 'standard' @@ -250,17 +270,6 @@ class CifData(SinglefileData): _values = None _ase = None - class Model(SinglefileData.Model): - formulae: t.Optional[t.List[str]] = MetadataField( - None, description='List of formulae contained in the CIF file.', exclude_to_orm=True - ) - spacegroup_numbers: t.Optional[t.List[str]] = MetadataField( - None, description='List of space group numbers of the structure.', exclude_to_orm=True - ) - md5: t.Optional[str] = MetadataField( - None, description='MD5 checksum of the file contents.', exclude_to_orm=True - ) - def __init__(self, ase=None, file=None, filename=None, values=None, scan_type=None, parse_policy=None, **kwargs): """Construct a new instance and set the contents to that of the file. @@ -372,7 +381,7 @@ def get_or_create(cls, filename, use_first=False, store_cif=True): return (cifs[0], False) raise ValueError( - 'More than one copy of a CIF file ' 'with the same MD5 has been found in ' 'the DB. pks={}'.format( + 'More than one copy of a CIF file with the same MD5 has been found in the DB. pks={}'.format( ','.join([str(i.pk) for i in cifs]) ) ) diff --git a/src/aiida/orm/nodes/data/code/abstract.py b/src/aiida/orm/nodes/data/code/abstract.py index eb72649b9c..a04412db84 100644 --- a/src/aiida/orm/nodes/data/code/abstract.py +++ b/src/aiida/orm/nodes/data/code/abstract.py @@ -19,11 +19,11 @@ from aiida.common import exceptions from aiida.common.folders import Folder from aiida.common.lang import type_check -from aiida.common.pydantic import MetadataField, get_metadata +from aiida.common.pydantic import MetadataField from aiida.orm import Computer from aiida.plugins import CalculationFactory -from ..data import Data +from ..data import Data, DataModel if t.TYPE_CHECKING: from aiida.engine import ProcessBuilder @@ -31,9 +31,71 @@ __all__ = ('AbstractCode',) +class AbstractCodeModel(DataModel, defer_build=True): + """Model describing required information to create an instance.""" + + label: str = MetadataField( + ..., + title='Label', + description='A unique label to identify the code by.', + short_name='-L', + ) + description: str = MetadataField( + '', + title='Description', + description='Human-readable description, ideally including version and compilation environment.', + short_name='-D', + ) + default_calc_job_plugin: t.Optional[str] = MetadataField( + None, + title='Default `CalcJob` plugin', + description='Entry point name of the default plugin (as listed in `verdi plugin list aiida.calculations`).', + short_name='-P', + ) + use_double_quotes: bool = MetadataField( + False, + title='Escape using double quotes', + description='Whether the executable and arguments of the code in the submission script should be escaped ' + 'with single or double quotes.', + ) + with_mpi: t.Optional[bool] = MetadataField( + None, + title='Run with MPI', + description='Whether the executable should be run as an MPI program. This option can be left unspecified ' + 'in which case `None` will be set and it is left up to the calculation job plugin or inputs ' + 'whether to run with MPI.', + ) + prepend_text: str = MetadataField( + '', + title='Prepend script', + description='Bash commands that should be prepended to the run line in all submit scripts for this code.', + option_cls=functools.partial( + TemplateInteractiveOption, + extension='.bash', + header='PREPEND_TEXT: if there is any bash commands that should be prepended to the executable call ' + 'in all submit scripts for this code, type that between the equal signs below and save the file.', + footer='All lines that start with `#=`: will be ignored.', + ), + ) + append_text: str = MetadataField( + '', + title='Append script', + description='Bash commands that should be appended to the run line in all submit scripts for this code.', + option_cls=functools.partial( + TemplateInteractiveOption, + extension='.bash', + header='APPEND_TEXT: if there is any bash commands that should be appended to the executable call ' + 'in all submit scripts for this code, type that between the equal signs below and save the file.', + footer='All lines that start with `#=`: will be ignored.', + ), + ) + + class AbstractCode(Data, metaclass=abc.ABCMeta): """Abstract data plugin representing an executable code.""" + Model = AbstractCodeModel + # Should become ``default_calc_job_plugin`` once ``Code`` is dropped in ``aiida-core==3.0`` _KEY_ATTRIBUTE_DEFAULT_CALC_JOB_PLUGIN: str = 'input_plugin' _KEY_ATTRIBUTE_APPEND_TEXT: str = 'append_text' @@ -43,65 +105,6 @@ class AbstractCode(Data, metaclass=abc.ABCMeta): _KEY_ATTRIBUTE_WRAP_CMDLINE_PARAMS: str = 'wrap_cmdline_params' _KEY_EXTRA_IS_HIDDEN: str = 'hidden' # Should become ``is_hidden`` once ``Code`` is dropped - class Model(Data.Model, defer_build=True): - """Model describing required information to create an instance.""" - - label: str = MetadataField( - ..., - title='Label', - description='A unique label to identify the code by.', - short_name='-L', - ) - description: str = MetadataField( - '', - title='Description', - description='Human-readable description, ideally including version and compilation environment.', - short_name='-D', - ) - default_calc_job_plugin: t.Optional[str] = MetadataField( - None, - title='Default `CalcJob` plugin', - description='Entry point name of the default plugin (as listed in `verdi plugin list aiida.calculations`).', - short_name='-P', - ) - use_double_quotes: bool = MetadataField( - False, - title='Escape using double quotes', - description='Whether the executable and arguments of the code in the submission script should be escaped ' - 'with single or double quotes.', - ) - with_mpi: t.Optional[bool] = MetadataField( - None, - title='Run with MPI', - description='Whether the executable should be run as an MPI program. This option can be left unspecified ' - 'in which case `None` will be set and it is left up to the calculation job plugin or inputs ' - 'whether to run with MPI.', - ) - prepend_text: str = MetadataField( - '', - title='Prepend script', - description='Bash commands that should be prepended to the run line in all submit scripts for this code.', - option_cls=functools.partial( - TemplateInteractiveOption, - extension='.bash', - header='PREPEND_TEXT: if there is any bash commands that should be prepended to the executable call ' - 'in all submit scripts for this code, type that between the equal signs below and save the file.', - footer='All lines that start with `#=`: will be ignored.', - ), - ) - append_text: str = MetadataField( - '', - title='Append script', - description='Bash commands that should be appended to the run line in all submit scripts for this code.', - option_cls=functools.partial( - TemplateInteractiveOption, - extension='.bash', - header='APPEND_TEXT: if there is any bash commands that should be appended to the executable call ' - 'in all submit scripts for this code, type that between the equal signs below and save the file.', - footer='All lines that start with `#=`: will be ignored.', - ), - ) - def __init__( self, default_calc_job_plugin: str | None = None, @@ -369,21 +372,17 @@ def _prepare_yaml(self, *args, **kwargs): """Export code to a YAML file.""" import yaml - code_data = {} - sort = kwargs.get('sort', False) - - for key, field in self.Model.model_fields.items(): - if get_metadata(field, 'exclude_from_cli'): - continue - elif (orm_to_model := get_metadata(field, 'orm_to_model')) is None: - value = getattr(self, key) - else: - value = orm_to_model(self, pathlib.Path.cwd() / f'{self.label}') - - # If the attribute is not set, for example ``with_mpi`` do not export it - # so that there are no null-values in the resulting YAML file - code_data[key] = value - return yaml.dump(code_data, sort_keys=sort, encoding='utf-8'), {} + code_data = self.orm_to_model_field_values( + repository_path=pathlib.Path.cwd() / f'{self.label}', + skip_read_only=True, + ) + + # Extras are not read-only, but we do not want to export them + code_data.pop('extras', None) + + # If the attribute is not set, for example ``with_mpi`` do not export it + # so that there are no null-values in the resulting YAML file + return yaml.dump(code_data, sort_keys=kwargs.get('sort', False), encoding='utf-8'), {} def _prepare_yml(self, *args, **kwargs): """Also allow for export as .yml""" diff --git a/src/aiida/orm/nodes/data/code/containerized.py b/src/aiida/orm/nodes/data/code/containerized.py index 85fd539cfa..b5265f8dd5 100644 --- a/src/aiida/orm/nodes/data/code/containerized.py +++ b/src/aiida/orm/nodes/data/code/containerized.py @@ -19,43 +19,46 @@ from aiida.common.lang import type_check from aiida.common.pydantic import MetadataField -from .installed import InstalledCode +from .installed import InstalledCode, InstalledCodeModel __all__ = ('ContainerizedCode',) +class ContainerizedCodeModel(InstalledCodeModel): + """Model describing required information to create an instance.""" + + engine_command: str = MetadataField( + ..., + title='Engine command', + description='The command to run the container. It must contain the placeholder {image_name} that will be ' + 'replaced with the `image_name`.', + short_name='-E', + priority=3, + ) + image_name: str = MetadataField( + ..., + title='Image name', + description='Name of the image container in which to the run the executable.', + short_name='-I', + priority=2, + ) + wrap_cmdline_params: bool = MetadataField( + False, + title='Wrap command line parameters', + description='Whether all command line parameters to be passed to the engine command should be wrapped in ' + 'a double quotes to form a single argument. This should be set to `True` for Docker.', + priority=1, + ) + + class ContainerizedCode(InstalledCode): """Data plugin representing an executable code in container on a remote computer.""" + Model = ContainerizedCodeModel + _KEY_ATTRIBUTE_ENGINE_COMMAND: str = 'engine_command' _KEY_ATTRIBUTE_IMAGE_NAME: str = 'image_name' - class Model(InstalledCode.Model): - """Model describing required information to create an instance.""" - - engine_command: str = MetadataField( - ..., - title='Engine command', - description='The command to run the container. It must contain the placeholder {image_name} that will be ' - 'replaced with the `image_name`.', - short_name='-E', - priority=3, - ) - image_name: str = MetadataField( - ..., - title='Image name', - description='Name of the image container in which to the run the executable.', - short_name='-I', - priority=2, - ) - wrap_cmdline_params: bool = MetadataField( - False, - title='Wrap command line parameters', - description='Whether all command line parameters to be passed to the engine command should be wrapped in ' - 'a double quotes to form a single argument. This should be set to `True` for Docker.', - priority=1, - ) - def __init__(self, engine_command: str, image_name: str, **kwargs): super().__init__(**kwargs) self.engine_command = engine_command diff --git a/src/aiida/orm/nodes/data/code/installed.py b/src/aiida/orm/nodes/data/code/installed.py index 3401f43ed2..cc5aefece5 100644 --- a/src/aiida/orm/nodes/data/code/installed.py +++ b/src/aiida/orm/nodes/data/code/installed.py @@ -19,7 +19,7 @@ import pathlib from typing import cast -from pydantic import field_serializer, field_validator +from pydantic import field_validator from aiida.common import exceptions from aiida.common.lang import type_check @@ -28,54 +28,60 @@ from aiida.orm import Computer from aiida.orm.entities import from_backend_entity -from .abstract import AbstractCode +from .abstract import AbstractCodeModel from .legacy import Code __all__ = ('InstalledCode',) +class InstalledCodeModel(AbstractCodeModel): + """Model describing required information to create an instance.""" + + computer: str = MetadataField( + title='Computer', + description='The label of the remote computer on which the executable resides.', + is_attribute=False, + orm_to_model=lambda node, _: cast(InstalledCode, node).computer.label, + model_to_orm=lambda model: cast(InstalledCodeModel, model).load_computer(), + short_name='-Y', + priority=2, + ) + filepath_executable: str = MetadataField( + title='Filepath executable', + description='Filepath of the executable on the remote computer.', + orm_to_model=lambda node, _: str(cast(InstalledCode, node).filepath_executable), + short_name='-X', + priority=1, + ) + + @field_validator('computer', mode='before') + @classmethod + def validate_computer(cls, value: str | int) -> str: + """Validate the ``computer`` field. + + :param value: The value to validate. + :return: The validated value. + :raises ValueError: If the value is not a string or integer. + """ + from aiida.orm import load_computer + + if isinstance(value, int): + try: + return load_computer(value).label + except exceptions.NotExistent as exception: + raise ValueError(f'No computer found for the given id: {value}') from exception + return value + + class InstalledCode(Code): """Data plugin representing an executable code on a remote computer.""" + Model = InstalledCodeModel # type: ignore[assignment] + _EMIT_CODE_DEPRECATION_WARNING: bool = False _KEY_ATTRIBUTE_FILEPATH_EXECUTABLE: str = 'filepath_executable' _SKIP_MODEL_INHERITANCE_CHECK: bool = True - class Model(AbstractCode.Model): - """Model describing required information to create an instance.""" - - computer: str = MetadataField( # type: ignore[assignment] - ..., - title='Computer', - description='The remote computer on which the executable resides.', - orm_to_model=lambda node, _: cast('InstalledCode', node).computer.label, - short_name='-Y', - priority=2, - ) - filepath_executable: str = MetadataField( - ..., - title='Filepath executable', - description='Filepath of the executable on the remote computer.', - orm_to_model=lambda node, _: str(cast('InstalledCode', node).filepath_executable), - short_name='-X', - priority=1, - ) - - @field_validator('computer') - @classmethod - def validate_computer(cls, value: str) -> Computer: - """Override the validator for the ``label`` of the base class since uniqueness is defined on full label.""" - from aiida.orm import load_computer - - try: - return load_computer(value) - except exceptions.NotExistent as exception: - raise ValueError(exception) from exception - - @field_serializer('computer') - def serialize_computer(self, computer: Computer, _info): - return computer.label - def __init__(self, computer: Computer, filepath_executable: str, **kwargs): """Construct a new instance. diff --git a/src/aiida/orm/nodes/data/code/legacy.py b/src/aiida/orm/nodes/data/code/legacy.py index b462d8ad7c..a477c7326a 100644 --- a/src/aiida/orm/nodes/data/code/legacy.py +++ b/src/aiida/orm/nodes/data/code/legacy.py @@ -18,11 +18,26 @@ from aiida.common.warnings import warn_deprecation from aiida.orm import Computer -from .abstract import AbstractCode +from .abstract import AbstractCode, AbstractCodeModel __all__ = ('Code',) +class CodeModel(AbstractCodeModel): + prepend_text: str = MetadataField( + '', + description='The code that will be put in the scheduler script before the execution of the code', + ) + append_text: str = MetadataField( + '', + description='The code that will be put in the scheduler script after the execution of the code', + ) + input_plugin: t.Optional[str] = MetadataField(description='The name of the input plugin to be used for this code') + local_executable: t.Optional[str] = MetadataField(description='Path to a local executable') + remote_exec_path: t.Optional[str] = MetadataField(description='Remote path to executable') + is_local: t.Optional[bool] = MetadataField(description='Whether the code is local or remote') + + class Code(AbstractCode): """A code entity. It can either be 'local', or 'remote'. @@ -39,21 +54,7 @@ class Code(AbstractCode): for the code to be run). """ - class Model(AbstractCode.Model): - prepend_text: str = MetadataField( - '', - description='The code that will be put in the scheduler script before the execution of the code', - ) - append_text: str = MetadataField( - '', - description='The code that will be put in the scheduler script after the execution of the code', - ) - input_plugin: t.Optional[str] = MetadataField( - description='The name of the input plugin to be used for this code' - ) - local_executable: t.Optional[str] = MetadataField(description='Path to a local executable') - remote_exec_path: t.Optional[str] = MetadataField(description='Remote path to executable') - is_local: t.Optional[bool] = MetadataField(description='Whether the code is local or remote') + Model = CodeModel def __init__(self, remote_computer_exec=None, local_executable=None, input_plugin_name=None, files=None, **kwargs): super().__init__(**kwargs) @@ -220,7 +221,7 @@ def get_code_helper(cls, label, machinename=None, backend=None): elif query.count() > 1: codes = query.all(flat=True) retstr = f"There are multiple codes with label '{label}', having IDs: " - retstr += f"{', '.join(sorted([str(c.pk) for c in codes]))}.\n" + retstr += f'{", ".join(sorted([str(c.pk) for c in codes]))}.\n' retstr += 'Relabel them (using their ID), or refer to them with their ID.' raise MultipleObjectsError(retstr) else: @@ -333,7 +334,7 @@ def _validate(self): if self.is_local(): if not self.get_local_executable(): raise exceptions.ValidationError( - 'You have to set which file is the local executable ' 'using the set_exec_filename() method' + 'You have to set which file is the local executable using the set_exec_filename() method' ) if self.get_local_executable() not in self.base.repository.list_object_names(): raise exceptions.ValidationError( diff --git a/src/aiida/orm/nodes/data/code/portable.py b/src/aiida/orm/nodes/data/code/portable.py index 911855318e..25f0b58115 100644 --- a/src/aiida/orm/nodes/data/code/portable.py +++ b/src/aiida/orm/nodes/data/code/portable.py @@ -21,6 +21,7 @@ import logging import pathlib +from typing import cast from aiida.common import exceptions from aiida.common.folders import Folder @@ -29,14 +30,14 @@ from aiida.common.typing import FilePath from aiida.orm import Computer -from .abstract import AbstractCode +from .abstract import AbstractCodeModel from .legacy import Code __all__ = ('PortableCode',) _LOGGER = logging.getLogger(__name__) -def _export_filpath_files_from_repo(portable_code: PortableCode, repository_path: pathlib.Path) -> str: +def _export_filepath_files_from_repo(portable_code: PortableCode, repository_path: pathlib.Path) -> str: for root, _, filenames in portable_code.base.repository.walk(): for filename in filenames: rel_path = str(root / filename) @@ -47,34 +48,40 @@ def _export_filpath_files_from_repo(portable_code: PortableCode, repository_path return str(repository_path) +class PortableCodeModel(AbstractCodeModel): + """Model describing required information to create an instance.""" + + filepath_executable: str = MetadataField( + ..., + title='Filepath executable', + description='Relative filepath of executable with directory of code files.', + short_name='-X', + priority=1, + orm_to_model=lambda node, _: str(cast(PortableCode, node).filepath_executable), + ) + filepath_files: str = MetadataField( + ..., + title='Code directory', + description='Filepath to directory containing code files.', + short_name='-F', + is_attribute=False, + priority=2, + orm_to_model=lambda node, kwargs: _export_filepath_files_from_repo( + cast(PortableCode, node), + kwargs.get('repository_path', pathlib.Path.cwd() / f'{cast(PortableCode, node).label}'), + ), + ) + + class PortableCode(Code): """Data plugin representing an executable code stored in AiiDA's storage.""" + Model = PortableCodeModel + _EMIT_CODE_DEPRECATION_WARNING: bool = False _KEY_ATTRIBUTE_FILEPATH_EXECUTABLE: str = 'filepath_executable' _SKIP_MODEL_INHERITANCE_CHECK: bool = True - class Model(AbstractCode.Model): - """Model describing required information to create an instance.""" - - filepath_executable: str = MetadataField( - ..., - title='Filepath executable', - description='Relative filepath of executable with directory of code files.', - short_name='-X', - priority=1, - orm_to_model=lambda node, _: str(node.filepath_executable), # type: ignore[attr-defined] - ) - filepath_files: str = MetadataField( - ..., - title='Code directory', - description='Filepath to directory containing code files.', - short_name='-F', - is_attribute=False, - priority=2, - orm_to_model=_export_filpath_files_from_repo, # type: ignore[arg-type] - ) - def __init__( self, filepath_executable: FilePath, @@ -201,7 +208,7 @@ def _prepare_yaml(self, *args, **kwargs): """Export code to a YAML file.""" result = super()._prepare_yaml(*args, **kwargs)[0] target = pathlib.Path().cwd() / f'{self.label}' - _export_filpath_files_from_repo(self, target) + _export_filepath_files_from_repo(self, target) _LOGGER.info(f'Repository files for PortableCode <{self.pk}> dumped to folder `{target}`.') return result, {} diff --git a/src/aiida/orm/nodes/data/data.py b/src/aiida/orm/nodes/data/data.py index 56b3dcbdbb..af24ae4c86 100644 --- a/src/aiida/orm/nodes/data/data.py +++ b/src/aiida/orm/nodes/data/data.py @@ -16,11 +16,20 @@ from aiida.common.pydantic import MetadataField from aiida.orm.entities import from_backend_entity -from ..node import Node +from ..node import Node, NodeModel __all__ = ('Data',) +class DataModel(NodeModel): + source: Optional[dict] = MetadataField( + None, + description='Source of the data.', + is_subscriptable=True, + exclude_to_orm=True, + ) + + class Data(Node): """The base class for all Data nodes. @@ -31,6 +40,8 @@ class Data(Node): Nodes are responsible for validating their content (see _validate method). """ + Model = DataModel + _source_attributes = ['db_name', 'db_uri', 'uri', 'id', 'version', 'extras', 'source_md5', 'description', 'license'] # Replace this with a dictionary in each subclass that, given a file @@ -46,14 +57,11 @@ class Data(Node): _storable = True _unstorable_message = 'storing for this node has been disabled' - class Model(Node.Model): - source: Optional[dict] = MetadataField( - None, description='Source of the data.', is_subscriptable=True, exclude_from_cli=True - ) - def __init__(self, *args, source=None, **kwargs): """Construct a new instance, setting the ``source`` attribute if provided as a keyword argument.""" + super().__init__(*args, **kwargs) + if source is not None: self.source = source @@ -113,7 +121,7 @@ def source(self, source): raise ValueError('Source must be supplied as a dictionary') unknown_attrs = tuple(set(source.keys()) - set(self._source_attributes)) if unknown_attrs: - raise KeyError(f"Unknown source parameters: {', '.join(unknown_attrs)}") + raise KeyError(f'Unknown source parameters: {", ".join(unknown_attrs)}') self.base.attributes.set('source', source) @@ -161,13 +169,13 @@ def _exportcontent(self, fileformat, main_file_name='', **kwargs): except KeyError: if exporters.keys(): raise ValueError( - 'The format {} is not implemented for {}. ' 'Currently implemented are: {}.'.format( + 'The format {} is not implemented for {}. Currently implemented are: {}.'.format( fileformat, self.__class__.__name__, ','.join(exporters.keys()) ) ) else: raise ValueError( - 'The format {} is not implemented for {}. ' 'No formats are implemented yet.'.format( + 'The format {} is not implemented for {}. No formats are implemented yet.'.format( fileformat, self.__class__.__name__ ) ) @@ -269,13 +277,13 @@ def importstring(self, inputstring, fileformat, **kwargs): except KeyError: if importers.keys(): raise ValueError( - 'The format {} is not implemented for {}. ' 'Currently implemented are: {}.'.format( + 'The format {} is not implemented for {}. Currently implemented are: {}.'.format( fileformat, self.__class__.__name__, ','.join(importers.keys()) ) ) else: raise ValueError( - 'The format {} is not implemented for {}. ' 'No formats are implemented yet.'.format( + 'The format {} is not implemented for {}. No formats are implemented yet.'.format( fileformat, self.__class__.__name__ ) ) @@ -326,13 +334,13 @@ def convert(self, object_format=None, *args): except KeyError: if converters.keys(): raise ValueError( - 'The format {} is not implemented for {}. ' 'Currently implemented are: {}.'.format( + 'The format {} is not implemented for {}. Currently implemented are: {}.'.format( object_format, self.__class__.__name__, ','.join(converters.keys()) ) ) else: raise ValueError( - 'The format {} is not implemented for {}. ' 'No formats are implemented yet.'.format( + 'The format {} is not implemented for {}. No formats are implemented yet.'.format( object_format, self.__class__.__name__ ) ) diff --git a/src/aiida/orm/nodes/data/dict.py b/src/aiida/orm/nodes/data/dict.py index 99673de6fd..86082270d6 100644 --- a/src/aiida/orm/nodes/data/dict.py +++ b/src/aiida/orm/nodes/data/dict.py @@ -16,13 +16,21 @@ from aiida.common import exceptions from aiida.common.pydantic import MetadataField +from . import data from .base import to_aiida_type -from .data import Data __all__ = ('Dict',) -class Dict(Data): +class DictModel(data.DataModel): + value: t.Dict[str, t.Any] = MetadataField( + description='Dictionary content.', + is_attribute=False, + is_subscriptable=True, + ) + + +class Dict(data.Data): """`Data` sub class to represent a dictionary. The dictionary contents of a `Dict` node are stored in the database as attributes. The dictionary @@ -50,12 +58,7 @@ class Dict(Data): Finally, all dictionary mutations will be forbidden once the node is stored. """ - class Model(Data.Model): - value: t.Dict[str, t.Any] = MetadataField( - description='Dictionary content.', - is_attribute=False, - is_subscriptable=True, - ) + Model = DictModel def __init__(self, value=None, **kwargs): """Initialise a ``Dict`` node instance. diff --git a/src/aiida/orm/nodes/data/enum.py b/src/aiida/orm/nodes/data/enum.py index 1c6fb41c79..6def13fc34 100644 --- a/src/aiida/orm/nodes/data/enum.py +++ b/src/aiida/orm/nodes/data/enum.py @@ -23,8 +23,8 @@ class Color(Enum): from aiida.common.lang import type_check from aiida.common.pydantic import MetadataField +from . import data from .base import to_aiida_type -from .data import Data __all__ = ('EnumData',) @@ -36,7 +36,14 @@ def _(value): return EnumData(member=value) -class EnumData(Data): +class EnumDataModel(data.DataModel): + member: Enum = MetadataField( + description='The member name.', + orm_to_model=lambda node, _: t.cast(EnumData, node).get_member(), + ) + + +class EnumData(data.Data): """Data plugin that allows to easily wrap an :class:`enum.Enum` member. The enum member is stored in the database by storing the value, name and the identifier (string that represents the @@ -46,16 +53,12 @@ class of the enumeration) in the ``KEY_NAME``, ``KEY_VALUE`` and ``KEY_IDENTIFIE the ``name`` and ``value`` properties which return the name and value of the enum member, respectively. """ + Model = EnumDataModel + KEY_NAME = 'name' KEY_VALUE = 'value' KEY_IDENTIFIER = 'identifier' - class Model(Data.Model): - member: Enum = MetadataField( - description='The member name.', - orm_to_model=lambda node, _: node.get_member(), # type: ignore[attr-defined] - ) - def __init__(self, member: Enum, *args, **kwargs): """Construct the node for the to enum member that is to be wrapped.""" type_check(member, Enum) diff --git a/src/aiida/orm/nodes/data/float.py b/src/aiida/orm/nodes/data/float.py index 88e535e067..9439b2648d 100644 --- a/src/aiida/orm/nodes/data/float.py +++ b/src/aiida/orm/nodes/data/float.py @@ -10,15 +10,26 @@ import numbers +from aiida.common.pydantic import MetadataField + +from . import numeric from .base import to_aiida_type -from .numeric import NumericType __all__ = ('Float',) -class Float(NumericType): +class FloatModel(numeric.NumericTypeModel): + value: float = MetadataField( + title='Float value', + description='The value of the float', + ) + + +class Float(numeric.NumericType): """`Data` sub class to represent a float value.""" + Model = FloatModel + _type = float diff --git a/src/aiida/orm/nodes/data/int.py b/src/aiida/orm/nodes/data/int.py index 43ffa918c3..420c253b91 100644 --- a/src/aiida/orm/nodes/data/int.py +++ b/src/aiida/orm/nodes/data/int.py @@ -10,15 +10,26 @@ import numbers +from aiida.common.pydantic import MetadataField + +from . import numeric from .base import to_aiida_type -from .numeric import NumericType __all__ = ('Int',) -class Int(NumericType): +class IntModel(numeric.NumericTypeModel): + value: int = MetadataField( + title='Integer value', + description='The value of the integer', + ) + + +class Int(numeric.NumericType): """`Data` sub class to represent an integer value.""" + Model = IntModel + _type = int diff --git a/src/aiida/orm/nodes/data/jsonable.py b/src/aiida/orm/nodes/data/jsonable.py index 24309abd72..0f75c67fa7 100644 --- a/src/aiida/orm/nodes/data/jsonable.py +++ b/src/aiida/orm/nodes/data/jsonable.py @@ -4,11 +4,11 @@ import json import typing -from pydantic import ConfigDict +from pydantic import ConfigDict, WithJsonSchema from aiida.common.pydantic import MetadataField -from .data import Data +from . import data __all__ = ('JsonableData',) @@ -18,7 +18,22 @@ class JsonSerializableProtocol(typing.Protocol): def as_dict(self) -> typing.MutableMapping[typing.Any, typing.Any]: ... -class JsonableData(Data): +class JsonableDataModel(data.DataModel): + model_config = ConfigDict(arbitrary_types_allowed=True) + + obj: typing.Annotated[ + JsonSerializableProtocol, + WithJsonSchema( + { + 'type': 'object', + 'title': 'JSON-serializable object', + 'description': 'The JSON-serializable object.', + } + ), + ] = MetadataField(description='The JSON-serializable object.') + + +class JsonableData(data.Data): """Data plugin that allows to easily wrap objects that are JSON-able. Any class that implements the ``as_dict`` method, returning a dictionary that is a JSON serializable representation @@ -50,9 +65,7 @@ class JsonableData(Data): environment, or an ``ImportError`` will be raised. """ - class Model(Data.Model): - model_config = ConfigDict(arbitrary_types_allowed=True) - obj: JsonSerializableProtocol = MetadataField(description='The JSON-serializable object.') + Model = JsonableDataModel def __init__(self, obj: JsonSerializableProtocol, *args, **kwargs): """Construct the node for the to be wrapped object.""" diff --git a/src/aiida/orm/nodes/data/list.py b/src/aiida/orm/nodes/data/list.py index f36dc70a8e..26dff4786d 100644 --- a/src/aiida/orm/nodes/data/list.py +++ b/src/aiida/orm/nodes/data/list.py @@ -14,21 +14,24 @@ from aiida.common.pydantic import MetadataField +from . import data from .base import to_aiida_type -from .data import Data __all__ = ('List',) -class List(Data, MutableSequence): +class ListModel(data.DataModel): + value: t.List[t.Any] = MetadataField( + description='Content of the data', + ) + + +class List(data.Data, MutableSequence): """`Data` sub class to represent a list.""" - _LIST_KEY = 'list' + Model = ListModel - class Model(Data.Model): - value: t.List[t.Any] = MetadataField( - description='Content of the data', - ) + _LIST_KEY = 'list' def __init__(self, value=None, **kwargs): """Initialise a ``List`` node instance. diff --git a/src/aiida/orm/nodes/data/numeric.py b/src/aiida/orm/nodes/data/numeric.py index f515140099..7b01153d82 100644 --- a/src/aiida/orm/nodes/data/numeric.py +++ b/src/aiida/orm/nodes/data/numeric.py @@ -8,7 +8,8 @@ ########################################################################### """Module for defintion of base `Data` sub class for numeric based data types.""" -from .base import BaseType, to_aiida_type +from . import base +from .base import to_aiida_type __all__ = ('NumericType',) @@ -39,9 +40,15 @@ def inner(self, other): return inner -class NumericType(BaseType): +class NumericTypeModel(base.BaseTypeModel): + """Placeholder model""" + + +class NumericType(base.BaseType): """Sub class of Data to store numbers, overloading common operators (``+``, ``*``, ...).""" + Model = NumericTypeModel + @_left_operator def __add__(self, other): return self + other diff --git a/src/aiida/orm/nodes/data/remote/base.py b/src/aiida/orm/nodes/data/remote/base.py index 8bae0fed02..54283bf377 100644 --- a/src/aiida/orm/nodes/data/remote/base.py +++ b/src/aiida/orm/nodes/data/remote/base.py @@ -13,35 +13,39 @@ import logging import os from pathlib import Path -from typing import Union +from typing import Optional, cast from aiida.common.pydantic import MetadataField from aiida.orm import AuthInfo from aiida.transports import Transport -from ..data import Data +from .. import data _logger = logging.getLogger(__name__) __all__ = ('RemoteData',) -class RemoteData(Data): +class RemoteDataModel(data.DataModel): + remote_path: Optional[str] = MetadataField( + None, + title='Remote path', + description='Filepath on the remote computer.', + orm_to_model=lambda node, _: cast(RemoteData, node).get_remote_path(), + ) + + +class RemoteData(data.Data): """Store a link to a file or folder on a remote machine. Remember to pass a computer! """ - KEY_EXTRA_CLEANED = 'cleaned' + Model = RemoteDataModel - class Model(Data.Model): - remote_path: Union[str, None] = MetadataField( - title='Remote path', - description='Filepath on the remote computer.', - orm_to_model=lambda node, _: node.get_remote_path(), - ) + KEY_EXTRA_CLEANED = 'cleaned' - def __init__(self, remote_path: Union[str, None] = None, **kwargs): + def __init__(self, remote_path: Optional[str] = None, **kwargs): super().__init__(**kwargs) if remote_path is not None: self.set_remote_path(remote_path) diff --git a/src/aiida/orm/nodes/data/remote/stash/base.py b/src/aiida/orm/nodes/data/remote/stash/base.py index 06a59269d5..47cf589b39 100644 --- a/src/aiida/orm/nodes/data/remote/stash/base.py +++ b/src/aiida/orm/nodes/data/remote/stash/base.py @@ -12,12 +12,16 @@ from aiida.common.lang import type_check from aiida.common.pydantic import MetadataField -from ...data import Data +from ... import data __all__ = ('RemoteStashData',) -class RemoteStashData(Data): +class RemoteStashDataModel(data.DataModel): + stash_mode: StashMode = MetadataField(description='The mode with which the data was stashed') + + +class RemoteStashData(data.Data): """Data plugin that models an archived folder on a remote computer. A stashed folder is essentially an instance of ``RemoteData`` that has been archived. Archiving in this context can @@ -34,10 +38,9 @@ class RemoteStashData(Data): methods of the class will only be available or function properly based on the ``stash_mode``. """ - _storable = False + Model = RemoteStashDataModel - class Model(Data.Model): - stash_mode: StashMode = MetadataField(description='The mode with which the data was stashed') + _storable = False def __init__(self, stash_mode: StashMode, **kwargs): """Construct a new instance diff --git a/src/aiida/orm/nodes/data/remote/stash/compress.py b/src/aiida/orm/nodes/data/remote/stash/compress.py index 70fadc3cc2..960c6df14b 100644 --- a/src/aiida/orm/nodes/data/remote/stash/compress.py +++ b/src/aiida/orm/nodes/data/remote/stash/compress.py @@ -14,26 +14,29 @@ from aiida.common.lang import type_check from aiida.common.pydantic import MetadataField -from .base import RemoteStashData +from . import base __all__ = ('RemoteStashCompressedData',) -class RemoteStashCompressedData(RemoteStashData): +class RemoteStashCompressedDataModel(base.RemoteStashDataModel): + target_basepath: str = MetadataField( + description='The the target basepath', + ) + source_list: List[str] = MetadataField( + description='The list of source files that were stashed', + ) + dereference: bool = MetadataField( + description='The format of the compression used when stashed', + ) + + +class RemoteStashCompressedData(base.RemoteStashData): """Data plugin that models a compressed stashed file on a remote computer.""" - _storable = True + Model = RemoteStashCompressedDataModel - class Model(RemoteStashData.Model): - target_basepath: str = MetadataField( - description='The the target basepath', - ) - source_list: List[str] = MetadataField( - description='The list of source files that were stashed', - ) - dereference: bool = MetadataField( - description='The format of the compression used when stashed', - ) + _storable = True def __init__( self, diff --git a/src/aiida/orm/nodes/data/remote/stash/custom.py b/src/aiida/orm/nodes/data/remote/stash/custom.py index d46cbc7ae1..b76c7ef342 100644 --- a/src/aiida/orm/nodes/data/remote/stash/custom.py +++ b/src/aiida/orm/nodes/data/remote/stash/custom.py @@ -14,19 +14,22 @@ from aiida.common.lang import type_check from aiida.common.pydantic import MetadataField -from .base import RemoteStashData +from . import base __all__ = ('RemoteStashCustomData',) -class RemoteStashCustomData(RemoteStashData): +class RemoteStashCustomDataModel(base.RemoteStashDataModel): + target_basepath: str = MetadataField(description='The the target basepath') + source_list: List[str] = MetadataField(description='The list of source files that were stashed') + + +class RemoteStashCustomData(base.RemoteStashData): """Data plugin that models stashed data on a remote computer, which was done via a custom script.""" - _storable = True + Model = RemoteStashCustomDataModel - class Model(RemoteStashData.Model): - target_basepath: str = MetadataField(description='The the target basepath') - source_list: List[str] = MetadataField(description='The list of source files that were stashed') + _storable = True def __init__( self, diff --git a/src/aiida/orm/nodes/data/remote/stash/folder.py b/src/aiida/orm/nodes/data/remote/stash/folder.py index 22afd57491..2a5bf20683 100644 --- a/src/aiida/orm/nodes/data/remote/stash/folder.py +++ b/src/aiida/orm/nodes/data/remote/stash/folder.py @@ -14,22 +14,25 @@ from aiida.common.lang import type_check from aiida.common.pydantic import MetadataField -from .base import RemoteStashData +from . import base __all__ = ('RemoteStashFolderData',) -class RemoteStashFolderData(RemoteStashData): +class RemoteStashFolderDataModel(base.RemoteStashDataModel): + target_basepath: str = MetadataField(description='The the target basepath') + source_list: List[str] = MetadataField(description='The list of source files that were stashed') + + +class RemoteStashFolderData(base.RemoteStashData): """Data plugin that models a folder with files of a completed calculation job that has been stashed through a copy. This data plugin can and should be used to stash files if and only if the stash mode is `StashMode.COPY`. """ - _storable = True + Model = RemoteStashFolderDataModel - class Model(RemoteStashData.Model): - target_basepath: str = MetadataField(description='The the target basepath') - source_list: List[str] = MetadataField(description='The list of source files that were stashed') + _storable = True def __init__(self, stash_mode: StashMode, target_basepath: str, source_list: List, **kwargs): """Construct a new instance diff --git a/src/aiida/orm/nodes/data/singlefile.py b/src/aiida/orm/nodes/data/singlefile.py index 742a5702c6..c82200c2f8 100644 --- a/src/aiida/orm/nodes/data/singlefile.py +++ b/src/aiida/orm/nodes/data/singlefile.py @@ -10,35 +10,60 @@ from __future__ import annotations +import base64 import contextlib import io import os import pathlib import typing as t +from pydantic import field_serializer, field_validator + from aiida.common import exceptions from aiida.common.pydantic import MetadataField from aiida.common.typing import FilePath -from .data import Data +from . import data __all__ = ('SinglefileData',) -class SinglefileData(Data): +class SinglefileDataModel(data.DataModel): + content: bytes = MetadataField( + description='The file content.', + model_to_orm=lambda model: io.BytesIO(t.cast(SinglefileData, model).content), + ) + filename: str = MetadataField( + 'file.txt', + description='The name of the stored file.', + ) + + @field_validator('content') + @classmethod + def _decode_content(cls, value: str | bytes) -> bytes: + """Decode base64 content if needed.""" + if isinstance(value, str): + try: + return base64.b64decode(value, validate=True) + except Exception as exc: + raise ValueError('if `content` is a string, it must be valid base64-encoded data') from exc + return value + + @field_serializer('content') + def _encode_content(self, value: bytes) -> str: + """Encode content as base64 string for serialization.""" + return base64.b64encode(value).decode() + + +class SinglefileData(data.Data): """Data class that can be used to store a single file in its repository.""" - DEFAULT_FILENAME = 'file.txt' + Model = SinglefileDataModel - class Model(Data.Model): - content: bytes = MetadataField( - description='The file content.', - model_to_orm=lambda model: io.BytesIO(model.content), # type: ignore[attr-defined] - ) - filename: t.Optional[str] = MetadataField(None, description='The filename. Defaults to `file.txt`.') + DEFAULT_FILENAME = 'file.txt' @classmethod - def from_string(cls, content: str, filename: str | pathlib.Path | None = None, **kwargs: t.Any) -> 'SinglefileData': + def from_string(cls, content: str, filename: str | pathlib.Path | None = None, **kwargs: t.Any) -> SinglefileData: """Construct a new instance and set ``content`` as its contents. :param content: The content as a string. @@ -47,9 +72,7 @@ def from_string(cls, content: str, filename: str | pathlib.Path | None = None, * return cls(io.StringIO(content), filename, **kwargs) @classmethod - def from_bytes( - cls, content: bytes, filename: str | pathlib.Path | None = None, **kwargs: t.Any - ) -> 'SinglefileData': + def from_bytes(cls, content: bytes, filename: str | pathlib.Path | None = None, **kwargs: t.Any) -> SinglefileData: """Construct a new instance and set ``content`` as its contents. :param content: The content as bytes. diff --git a/src/aiida/orm/nodes/data/str.py b/src/aiida/orm/nodes/data/str.py index 66b631ef7c..616c846a11 100644 --- a/src/aiida/orm/nodes/data/str.py +++ b/src/aiida/orm/nodes/data/str.py @@ -8,14 +8,26 @@ ########################################################################### """`Data` sub class to represent a string value.""" -from .base import BaseType, to_aiida_type +from aiida.common.pydantic import MetadataField + +from . import base +from .base import to_aiida_type __all__ = ('Str',) -class Str(BaseType): +class StrModel(base.BaseTypeModel): + value: str = MetadataField( + title='String value', + description='The value of the string', + ) + + +class Str(base.BaseType): """`Data` sub class to represent a string value.""" + Model = StrModel + _type = str diff --git a/src/aiida/orm/nodes/data/structure.py b/src/aiida/orm/nodes/data/structure.py index 0e3e09ef5b..9eccc998e4 100644 --- a/src/aiida/orm/nodes/data/structure.py +++ b/src/aiida/orm/nodes/data/structure.py @@ -10,17 +10,21 @@ functions to operate on them. """ +from __future__ import annotations + import copy import functools import itertools import json import typing as t +from pydantic import field_validator + from aiida.common.constants import elements from aiida.common.exceptions import UnsupportedSpeciesError from aiida.common.pydantic import MetadataField -from .data import Data +from . import data __all__ = ('Kind', 'Site', 'StructureData') @@ -503,7 +507,7 @@ def get_symbols_string(symbols, weights): pieces.append(f'{symbol}{weight:4.2f}') if has_vacancies(weights): pieces.append(f'X{1.0 - sum(weights):4.2f}') - return f"{{{''.join(sorted(pieces))}}}" + return f'{{{"".join(sorted(pieces))}}}' def has_vacancies(weights): @@ -657,13 +661,34 @@ def atom_kinds_to_html(atom_kind): return html_formula -class StructureData(Data): +class StructureDataModel(data.DataModel): + pbc1: bool = MetadataField(description='Whether periodic in the a direction') + pbc2: bool = MetadataField(description='Whether periodic in the b direction') + pbc3: bool = MetadataField(description='Whether periodic in the c direction') + cell: t.List[t.List[float]] = MetadataField(description='The cell parameters') + kinds: t.List[dict] = MetadataField(description='The kinds of atoms') + sites: t.List[dict] = MetadataField(description='The atomic sites') + + @field_validator('kinds', mode='before') + @classmethod + def _validate_kinds(cls, value: t.List[Kind | dict[str, t.Any]]) -> t.List[t.Dict]: + return [kind.get_raw() if isinstance(kind, Kind) else kind for kind in value] + + @field_validator('sites', mode='before') + @classmethod + def _validate_sites(cls, value: t.List[Site | dict[str, t.Any]]) -> t.List[t.Dict]: + return [site.get_raw() if isinstance(site, Site) else site for site in value] + + +class StructureData(data.Data): """Data class that represents an atomic structure. The data is organized as a collection of sites together with a cell, the boundary conditions (whether they are periodic or not) and other related useful information. """ + Model = StructureDataModel + _set_incompatibilities = [ ('ase', 'cell'), ('ase', 'pbc'), @@ -684,14 +709,6 @@ class StructureData(Data): _dimensionality_label = {0: '', 1: 'length', 2: 'surface', 3: 'volume'} _internal_kind_tags = None - class Model(Data.Model): - pbc1: bool = MetadataField(description='Whether periodic in the a direction') - pbc2: bool = MetadataField(description='Whether periodic in the b direction') - pbc3: bool = MetadataField(description='Whether periodic in the c direction') - cell: t.List[t.List[float]] = MetadataField(description='The cell parameters') - kinds: t.Optional[t.List[dict]] = MetadataField(description='The kinds of atoms') - sites: t.Optional[t.List[dict]] = MetadataField(description='The atomic sites') - def __init__( self, cell=None, @@ -1371,7 +1388,7 @@ def append_atom(self, **kwargs): if aseatom is not None: if kwargs: raise ValueError( - "If you pass 'ase' as a parameter to " 'append_atom, you cannot pass any further' 'parameter' + "If you pass 'ase' as a parameter to append_atom, you cannot pass any further parameter" ) position = aseatom.position kind = Kind(ase=aseatom) @@ -2127,9 +2144,7 @@ def weights(self, value): weights_tuple = _create_weights_tuple(value) if len(weights_tuple) != len(self._symbols): - raise ValueError( - 'Cannot change the number of weights. Use the ' 'set_symbols_and_weights function instead.' - ) + raise ValueError('Cannot change the number of weights. Use the set_symbols_and_weights function instead.') validate_weights_tuple(weights_tuple, _SUM_THRESHOLD) self._weights = weights_tuple @@ -2182,9 +2197,7 @@ def symbols(self, value): symbols_tuple = _create_symbols_tuple(value) if len(symbols_tuple) != len(self._weights): - raise ValueError( - 'Cannot change the number of symbols. Use the ' 'set_symbols_and_weights function instead.' - ) + raise ValueError('Cannot change the number of symbols. Use the set_symbols_and_weights function instead.') validate_symbols_tuple(symbols_tuple) self._symbols = symbols_tuple diff --git a/src/aiida/orm/nodes/node.py b/src/aiida/orm/nodes/node.py index 129a059d89..3d1a7e392c 100644 --- a/src/aiida/orm/nodes/node.py +++ b/src/aiida/orm/nodes/node.py @@ -12,8 +12,23 @@ import base64 import datetime +from collections.abc import Iterable from functools import cached_property -from typing import TYPE_CHECKING, Any, ClassVar, Dict, Generic, Iterator, List, NoReturn, Optional, Tuple, Type, TypeVar +from typing import ( + TYPE_CHECKING, + Any, + ClassVar, + Dict, + Generic, + Iterator, + List, + NoReturn, + Optional, + Tuple, + Type, + TypeVar, + cast, +) from uuid import UUID from typing_extensions import Self @@ -32,8 +47,7 @@ ) from ..computers import Computer -from ..entities import Collection as EntityCollection -from ..entities import Entity, from_backend_entity +from ..entities import Entity, EntityCollection, EntityModel, from_backend_entity from ..extras import EntityExtras from ..querybuilder import QueryBuilder from ..users import User @@ -51,16 +65,29 @@ from ..implementation.nodes import BackendNode from .repository import NodeRepository -__all__ = ('Node',) +__all__ = ('Node', 'NodeType') NodeType = TypeVar('NodeType', bound='Node') +class UnhandledNodeAttributesError(Exception): + """Exception raised when any node attributes are not handled prior to the Node constructor.""" + + def __init__(self, attributes: Iterable[str], class_name: str) -> None: + bullet_list = '\n'.join(f' • {attr}' for attr in attributes) + message = ( + f'\nThe following attributes must be handled in a constructor prior to the Node class:\n' + f'{bullet_list}\n\n' + f'Consider implementing a constructor in {class_name} to handle the listed attributes.' + ) + super().__init__(message) + + class NodeCollection(EntityCollection[NodeType], Generic[NodeType]): """The collection of nodes.""" @staticmethod - def _entity_base_cls() -> Type['Node']: # type: ignore[override] + def _entity_base_cls() -> Type[Node]: # type: ignore[override] return Node def delete(self, pk: int) -> None: @@ -105,44 +132,146 @@ def iter_repo_keys( class NodeBase: """A namespace for node related functionality, that is not directly related to its user-facing properties.""" - def __init__(self, node: 'Node') -> None: + def __init__(self, node: Node) -> None: """Construct a new instance of the base namespace.""" self._node = node @cached_property - def repository(self) -> 'NodeRepository': + def repository(self) -> NodeRepository: """Return the repository for this node.""" from .repository import NodeRepository return NodeRepository(self._node) @cached_property - def caching(self) -> 'NodeCaching': + def caching(self) -> NodeCaching: """Return an interface to interact with the caching of this node.""" return self._node._CLS_NODE_CACHING(self._node) @cached_property - def comments(self) -> 'NodeComments': + def comments(self) -> NodeComments: """Return an interface to interact with the comments of this node.""" return NodeComments(self._node) @cached_property - def attributes(self) -> 'NodeAttributes': + def attributes(self) -> NodeAttributes: """Return an interface to interact with the attributes of this node.""" return NodeAttributes(self._node) @cached_property - def extras(self) -> 'EntityExtras': + def extras(self) -> EntityExtras: """Return an interface to interact with the extras of this node.""" return EntityExtras(self._node) @cached_property - def links(self) -> 'NodeLinks': + def links(self) -> NodeLinks: """Return an interface to interact with the links of this node.""" return self._node._CLS_NODE_LINKS(self._node) -class Node(Entity['BackendNode', NodeCollection['Node']], metaclass=AbstractNodeMeta): +class NodeModel(EntityModel): + uuid: UUID = MetadataField( + description='The UUID of the node', + is_attribute=False, + exclude_to_orm=True, + ) + node_type: str = MetadataField( + description='The type of the node', + is_attribute=False, + exclude_to_orm=True, + ) + process_type: Optional[str] = MetadataField( + None, + description='The process type of the node', + is_attribute=False, + exclude_to_orm=True, + ) + repository_metadata: Dict[str, Any] = MetadataField( + default_factory=dict, + description='Virtual hierarchy of the file repository.', + is_attribute=False, + orm_to_model=lambda node, _: cast(Node, node).base.repository.metadata, + exclude_to_orm=True, + ) + ctime: datetime.datetime = MetadataField( + description='The creation time of the node', + is_attribute=False, + exclude_to_orm=True, + ) + mtime: datetime.datetime = MetadataField( + description='The modification time of the node', + is_attribute=False, + exclude_to_orm=True, + ) + label: str = MetadataField( + '', + description='The node label', + is_attribute=False, + ) + description: str = MetadataField( + '', + description='The node description', + is_attribute=False, + ) + attributes: Dict[str, Any] = MetadataField( + default_factory=dict, + description='The node attributes', + is_attribute=False, + orm_to_model=lambda node, _: cast(Node, node).base.attributes.all, + is_subscriptable=True, + exclude_to_orm=True, + ) + extras: Dict[str, Any] = MetadataField( + default_factory=dict, + description='The node extras', + is_attribute=False, + orm_to_model=lambda node, _: cast(Node, node).base.extras.all, + is_subscriptable=True, + ) + computer: Optional[str] = MetadataField( + None, + description='The label of the computer', + is_attribute=False, + orm_to_model=lambda node, _: cast(Node, node).get_computer_label(), + model_to_orm=lambda model: cast(NodeModel, model).load_computer(), + exclude_to_orm=True, + ) + user: int = MetadataField( + description='The PK of the user who owns the node', + is_attribute=False, + orm_to_model=lambda node, _: cast(Node, node).user.pk, + orm_class=User, + exclude_to_orm=True, + ) + repository_content: dict[str, bytes] = MetadataField( + default_factory=dict, + description='Dictionary of file repository content. Keys are relative filepaths and values are binary file ' + 'contents encoded as base64.', + is_attribute=False, + orm_to_model=lambda node, kwargs: { + key: base64.encodebytes(content) + for key, content in cast(Node, node).base.repository.serialize_content().items() + } + if kwargs.get('serialize_repository_content') + else {}, + exclude_to_orm=True, + ) + + def load_computer(self) -> Computer: + """Load the computer instance. + + :return: The computer instance. + :raises ValueError: If the computer does not exist. + """ + from aiida.orm import load_computer + + try: + return load_computer(self.computer) + except exceptions.NotExistent as exception: + raise ValueError(exception) from exception + + +class Node(Entity['BackendNode', NodeCollection['Node'], NodeModel], metaclass=AbstractNodeMeta): """Base class for all nodes in AiiDA. Stores attributes starting with an underscore. @@ -157,6 +286,8 @@ class Node(Entity['BackendNode', NodeCollection['Node']], metaclass=AbstractNode the 'type' field. """ + Model = NodeModel + _CLS_COLLECTION = NodeCollection['Node'] _CLS_NODE_LINKS = NodeLinks _CLS_NODE_CACHING = NodeCaching @@ -195,95 +326,6 @@ def _query_type_string(cls) -> str: # noqa: N805 _storable = False _unstorable_message = 'only Data, WorkflowNode, CalculationNode or their subclasses can be stored' - class Model(Entity.Model): - uuid: Optional[str] = MetadataField( - None, description='The UUID of the node', is_attribute=False, exclude_to_orm=True, exclude_from_cli=True - ) - node_type: Optional[str] = MetadataField( - None, description='The type of the node', is_attribute=False, exclude_to_orm=True, exclude_from_cli=True - ) - process_type: Optional[str] = MetadataField( - None, - description='The process type of the node', - is_attribute=False, - exclude_to_orm=True, - exclude_from_cli=True, - ) - repository_metadata: Optional[Dict[str, Any]] = MetadataField( - None, - description='Virtual hierarchy of the file repository.', - is_attribute=False, - orm_to_model=lambda node, _: node.base.repository.metadata, # type: ignore[attr-defined] - exclude_to_orm=True, - exclude_from_cli=True, - ) - ctime: Optional[datetime.datetime] = MetadataField( - None, - description='The creation time of the node', - is_attribute=False, - exclude_to_orm=True, - exclude_from_cli=True, - ) - mtime: Optional[datetime.datetime] = MetadataField( - None, - description='The modification time of the node', - is_attribute=False, - exclude_to_orm=True, - exclude_from_cli=True, - ) - label: Optional[str] = MetadataField( - None, description='The node label', is_attribute=False, exclude_from_cli=True - ) - description: Optional[str] = MetadataField( - None, description='The node description', is_attribute=False, exclude_from_cli=True - ) - attributes: Optional[Dict[str, Any]] = MetadataField( - None, - description='The node attributes', - is_attribute=False, - orm_to_model=lambda node, _: node.base.attributes.all, # type: ignore[attr-defined] - is_subscriptable=True, - exclude_from_cli=True, - exclude_to_orm=True, - ) - extras: Optional[Dict[str, Any]] = MetadataField( - None, - description='The node extras', - is_attribute=False, - orm_to_model=lambda node, _: node.base.extras.all, # type: ignore[attr-defined] - is_subscriptable=True, - exclude_from_cli=True, - exclude_to_orm=True, - ) - computer: Optional[int] = MetadataField( - None, - description='The PK of the computer', - is_attribute=False, - orm_to_model=lambda node, _: node.computer.pk if node.computer else None, # type: ignore[attr-defined] - orm_class=Computer, - exclude_from_cli=True, - ) - user: Optional[int] = MetadataField( - None, - description='The PK of the user who owns the node', - is_attribute=False, - orm_to_model=lambda node, _: node.user.pk, # type: ignore[attr-defined] - orm_class=User, - exclude_from_cli=True, - ) - repository_content: Optional[dict[str, bytes]] = MetadataField( - None, - description='Dictionary of file repository content. Keys are relative filepaths and values are binary file ' - 'contents encoded as base64.', - is_attribute=False, - orm_to_model=lambda node, _: { - key: base64.encodebytes(content) - for key, content in node.base.repository.serialize_content().items() # type: ignore[attr-defined] - }, - exclude_from_cli=True, - exclude_to_orm=True, - ) - def __init__( self, backend: Optional['StorageBackend'] = None, @@ -292,6 +334,13 @@ def __init__( extras: Optional[Dict[str, Any]] = None, **kwargs: Any, ) -> None: + # We verify here that all attributes are handled in a constructor prior to the root + # Node class (here), gracefully rejecting them otherwise. + node_keys = set(NodeModel.model_fields.keys()) + unhandled_keys = {key for key in kwargs if key not in node_keys} + if unhandled_keys: + raise UnhandledNodeAttributesError(unhandled_keys, self.__class__.__name__) + backend = backend or get_manager().get_profile_storage() if computer and not computer.is_stored: @@ -307,11 +356,12 @@ def __init__( node_type=self.class_node_type, user=user.backend_entity, computer=backend_computer, **kwargs ) super().__init__(backend_entity) + if extras is not None: self.base.extras.set_many(extras) @classmethod - def _from_model(cls, model: Model) -> Self: # type: ignore[override] + def from_model(cls, model: NodeModel) -> Self: # type: ignore[override] """Return an entity instance from an instance of its model.""" fields = cls.model_to_orm_field_values(model) @@ -615,7 +665,7 @@ def _verify_are_parents_stored(self) -> None: f'Cannot store because source node of link triple {link_triple} is not stored' ) - def _store_from_cache(self, cache_node: 'Node') -> None: + def _store_from_cache(self, cache_node: Node) -> None: """Store this node from an existing cache node. .. note:: @@ -648,7 +698,7 @@ def _store_from_cache(self, cache_node: 'Node') -> None: self._add_outputs_from_cache(cache_node) self.base.extras.set(self.base.caching.CACHED_FROM_KEY, cache_node.uuid) - def _add_outputs_from_cache(self, cache_node: 'Node') -> None: + def _add_outputs_from_cache(self, cache_node: Node) -> None: """Replicate the output links and nodes from the cached node onto this node.""" for entry in cache_node.base.links.get_outgoing(link_type=LinkType.CREATE): new_node = entry.node.clone() @@ -662,6 +712,15 @@ def get_description(self) -> str: """ return '' + def get_computer_label(self) -> Optional[str]: + """Get the label of the computer of this node. + + :return: The computer label or None if no computer is set. + """ + if self.computer is None: + return None + return self.computer.label + @property def is_valid_cache(self) -> bool: """Hook to exclude certain ``Node`` classes from being considered a valid cache. diff --git a/src/aiida/orm/nodes/process/calculation/calcjob.py b/src/aiida/orm/nodes/process/calculation/calcjob.py index a526fc3b9c..48639fbe69 100644 --- a/src/aiida/orm/nodes/process/calculation/calcjob.py +++ b/src/aiida/orm/nodes/process/calculation/calcjob.py @@ -8,8 +8,10 @@ ########################################################################### """Module with `Node` sub class for calculation job processes.""" +from __future__ import annotations + import datetime -from typing import TYPE_CHECKING, Any, AnyStr, Dict, List, Optional, Sequence, Tuple, Type, Union +from typing import TYPE_CHECKING, Any, AnyStr, Dict, List, Optional, Sequence, Tuple, Type, Union, cast from aiida.common import exceptions from aiida.common.datastructures import CalcJobState @@ -17,7 +19,7 @@ from aiida.common.pydantic import MetadataField from ..process import ProcessNodeCaching -from .calculation import CalculationNode +from . import calculation if TYPE_CHECKING: from aiida.orm import FolderData @@ -48,9 +50,64 @@ def get_objects_to_hash(self) -> List[Any]: return objects -class CalcJobNode(CalculationNode): +class CalcJobNodeModel(calculation.CalculationNodeModel): + scheduler_state: Optional[str] = MetadataField( + None, + description='The state of the scheduler', + orm_to_model=lambda node, _: cast(CalcJobNode, node).get_scheduler_state(), + ) + state: Optional[str] = MetadataField( + None, + description='The active state of the calculation job', + orm_to_model=lambda node, _: cast(CalcJobNode, node).get_state(), + ) + remote_workdir: Optional[str] = MetadataField( + None, + description='The path to the remote (on cluster) scratch folder', + orm_to_model=lambda node, _: cast(CalcJobNode, node).get_remote_workdir(), + ) + job_id: Optional[str] = MetadataField( + None, + description='The scheduler job id', + orm_to_model=lambda node, _: cast(CalcJobNode, node).get_job_id(), + ) + scheduler_lastchecktime: Optional[datetime.datetime] = MetadataField( + None, + description='The last time the scheduler was checked, in isoformat', + orm_to_model=lambda node, _: cast(CalcJobNode, node).get_scheduler_lastchecktime(), + ) + last_job_info: Optional[dict] = MetadataField( + None, + description='The last job info returned by the scheduler', + orm_to_model=lambda node, _: dict(cast(CalcJobNode, node).get_last_job_info() or {}), + ) + detailed_job_info: Optional[dict] = MetadataField( + None, + description='The detailed job info returned by the scheduler', + orm_to_model=lambda node, _: cast(CalcJobNode, node).get_detailed_job_info(), + ) + retrieve_list: Optional[Sequence[Union[str, Tuple[str, str, int]]]] = MetadataField( + None, + description='The list of files to retrieve from the remote cluster', + orm_to_model=lambda node, _: cast(CalcJobNode, node).get_retrieve_list(), + ) + retrieve_temporary_list: Optional[Sequence[Union[str, Tuple[str, str, int]]]] = MetadataField( + None, + description='The list of temporary files to retrieve from the remote cluster', + orm_to_model=lambda node, _: cast(CalcJobNode, node).get_retrieve_temporary_list(), + ) + imported: Optional[bool] = MetadataField( + None, + description='Whether the node has been migrated', + orm_to_model=lambda node, _: cast(CalcJobNode, node).is_imported, + ) + + +class CalcJobNode(calculation.CalculationNode): """ORM class for all nodes representing the execution of a CalcJob.""" + Model = CalcJobNodeModel + _CLS_NODE_CACHING = CalcJobNodeCaching IMMIGRATED_KEY = 'imported' @@ -64,44 +121,6 @@ class CalcJobNode(CalculationNode): SCHEDULER_LAST_JOB_INFO_KEY = 'last_job_info' SCHEDULER_DETAILED_JOB_INFO_KEY = 'detailed_job_info' - class Model(CalculationNode.Model): - scheduler_state: Optional[str] = MetadataField( - description='The state of the scheduler', orm_to_model=lambda node, _: node.get_scheduler_state() - ) - state: Optional[str] = MetadataField( - description='The active state of the calculation job', orm_to_model=lambda node, _: node.get_state() - ) - remote_workdir: Optional[str] = MetadataField( - description='The path to the remote (on cluster) scratch folder', - orm_to_model=lambda node, _: node.get_remote_workdir(), - ) - job_id: Optional[str] = MetadataField( - description='The scheduler job id', orm_to_model=lambda node, _: node.get_job_id() - ) - scheduler_lastchecktime: Optional[datetime.datetime] = MetadataField( - description='The last time the scheduler was checked, in isoformat', - orm_to_model=lambda node, _: node.get_scheduler_lastchecktime(), - ) - last_job_info: Optional[dict] = MetadataField( - description='The last job info returned by the scheduler', - orm_to_model=lambda node, _: dict(node.get_last_job_info() or {}), - ) - detailed_job_info: Optional[dict] = MetadataField( - description='The detailed job info returned by the scheduler', - orm_to_model=lambda node, _: node.get_detailed_job_info(), - ) - retrieve_list: Optional[List[str]] = MetadataField( - description='The list of files to retrieve from the remote cluster', - orm_to_model=lambda node, _: node.get_retrieve_list(), - ) - retrieve_temporary_list: Optional[List[str]] = MetadataField( - description='The list of temporary files to retrieve from the remote cluster', - orm_to_model=lambda node, _: node.get_retrieve_temporary_list(), - ) - imported: Optional[bool] = MetadataField( - description='Whether the node has been migrated', orm_to_model=lambda node, _: node.is_imported - ) - # An optional entry point for a CalculationTools instance _tools = None @@ -259,7 +278,7 @@ def get_remote_workdir(self) -> Optional[str]: return self.base.attributes.get(self.REMOTE_WORKDIR_KEY, None) @staticmethod - def _validate_retrieval_directive(directives: Sequence[Union[str, Tuple[str, str, str]]]) -> None: + def _validate_retrieval_directive(directives: Sequence[Union[str, Tuple[str, str, int]]]) -> None: """Validate a list or tuple of file retrieval directives. :param directives: a list or tuple of file retrieval directives @@ -286,7 +305,7 @@ def _validate_retrieval_directive(directives: Sequence[Union[str, Tuple[str, str if not isinstance(directive[2], (int, type(None))): raise ValueError('invalid directive, third element has to be an integer representing the depth') - def set_retrieve_list(self, retrieve_list: Sequence[Union[str, Tuple[str, str, str]]]) -> None: + def set_retrieve_list(self, retrieve_list: Sequence[Union[str, Tuple[str, str, int]]]) -> None: """Set the retrieve list. This list of directives will instruct the daemon what files to retrieve after the calculation has completed. @@ -297,14 +316,14 @@ def set_retrieve_list(self, retrieve_list: Sequence[Union[str, Tuple[str, str, s self._validate_retrieval_directive(retrieve_list) self.base.attributes.set(self.RETRIEVE_LIST_KEY, retrieve_list) - def get_retrieve_list(self) -> Optional[Sequence[Union[str, Tuple[str, str, str]]]]: + def get_retrieve_list(self) -> Optional[Sequence[Union[str, Tuple[str, str, int]]]]: """Return the list of files/directories to be retrieved on the cluster after the calculation has completed. :return: a list of file directives """ return self.base.attributes.get(self.RETRIEVE_LIST_KEY, None) - def set_retrieve_temporary_list(self, retrieve_temporary_list: Sequence[Union[str, Tuple[str, str, str]]]) -> None: + def set_retrieve_temporary_list(self, retrieve_temporary_list: Sequence[Union[str, Tuple[str, str, int]]]) -> None: """Set the retrieve temporary list. The retrieve temporary list stores files that are retrieved after completion and made available during parsing @@ -315,7 +334,7 @@ def set_retrieve_temporary_list(self, retrieve_temporary_list: Sequence[Union[st self._validate_retrieval_directive(retrieve_temporary_list) self.base.attributes.set(self.RETRIEVE_TEMPORARY_LIST_KEY, retrieve_temporary_list) - def get_retrieve_temporary_list(self) -> Optional[Sequence[Union[str, Tuple[str, str, str]]]]: + def get_retrieve_temporary_list(self) -> Optional[Sequence[Union[str, Tuple[str, str, int]]]]: """Return list of files to be retrieved from the cluster which will be available during parsing. :return: a list of file directives @@ -338,7 +357,7 @@ def get_job_id(self) -> Optional[str]: """ return self.base.attributes.get(self.SCHEDULER_JOB_ID_KEY, None) - def set_scheduler_state(self, state: 'JobState') -> None: + def set_scheduler_state(self, state: JobState) -> None: """Set the scheduler state. :param state: an instance of `JobState` @@ -423,7 +442,7 @@ def get_last_job_info(self) -> Optional['JobInfo']: return job_info - def get_authinfo(self) -> 'AuthInfo': + def get_authinfo(self) -> AuthInfo: """Return the `AuthInfo` that is configured for the `Computer` set for this node. :return: `AuthInfo` @@ -435,7 +454,7 @@ def get_authinfo(self) -> 'AuthInfo': return computer.get_authinfo(self.user) - def get_transport(self) -> 'Transport': + def get_transport(self) -> Transport: """Return the transport for this calculation. :return: Transport configured @@ -443,7 +462,7 @@ def get_transport(self) -> 'Transport': """ return self.get_authinfo().get_transport() - def get_parser_class(self) -> Optional[Type['Parser']]: + def get_parser_class(self) -> Optional[Type[Parser]]: """Return the output parser object for this calculation or None if no parser is set. :return: a `Parser` class. @@ -463,7 +482,7 @@ def link_label_retrieved(self) -> str: """Return the link label used for the retrieved FolderData node.""" return 'retrieved' - def get_retrieved_node(self) -> Optional['FolderData']: + def get_retrieved_node(self) -> Optional[FolderData]: """Return the retrieved data folder. :return: the retrieved FolderData node or None if not found @@ -480,7 +499,7 @@ def get_retrieved_node(self) -> Optional['FolderData']: return None @property - def res(self) -> 'CalcJobResultManager': + def res(self) -> CalcJobResultManager: """To be used to get direct access to the parsed parameters. :return: an instance of the CalcJobResultManager. diff --git a/src/aiida/orm/nodes/process/calculation/calculation.py b/src/aiida/orm/nodes/process/calculation/calculation.py index 1bbd234e00..a1b2390fdb 100644 --- a/src/aiida/orm/nodes/process/calculation/calculation.py +++ b/src/aiida/orm/nodes/process/calculation/calculation.py @@ -11,14 +11,20 @@ from aiida.common.links import LinkType from aiida.orm.utils.managers import NodeLinksManager -from ..process import ProcessNode +from .. import process __all__ = ('CalculationNode',) -class CalculationNode(ProcessNode): +class CalculationNodeModel(process.ProcessNodeModel): + """Placeholder model.""" + + +class CalculationNode(process.ProcessNode): """Base class for all nodes representing the execution of a calculation process.""" + Model = CalculationNodeModel + _storable = True # Calculation nodes are storable _cachable = True # Calculation nodes can be cached from _unstorable_message = 'storing for this node has been disabled' diff --git a/src/aiida/orm/nodes/process/process.py b/src/aiida/orm/nodes/process/process.py index 37369d14f6..3745066c8c 100644 --- a/src/aiida/orm/nodes/process/process.py +++ b/src/aiida/orm/nodes/process/process.py @@ -8,6 +8,8 @@ ########################################################################### """Module with `Node` sub class for processes.""" +from __future__ import annotations + import enum from pathlib import Path from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Type, Union @@ -18,10 +20,10 @@ from aiida.common.lang import classproperty from aiida.common.links import LinkType from aiida.common.pydantic import MetadataField -from aiida.orm.utils.mixins import Sealable +from aiida.orm.utils.mixins import Sealable, SealableModel +from .. import node from ..caching import NodeCaching -from ..node import Node, NodeLinks if TYPE_CHECKING: from aiida.engine.processes import ExitCode, Process @@ -99,10 +101,10 @@ def get_objects_to_hash(self) -> List[Any]: return res -class ProcessNodeLinks(NodeLinks): +class ProcessNodeLinks(node.NodeLinks): """Interface for links of a node instance.""" - def validate_incoming(self, source: Node, link_type: LinkType, link_label: str) -> None: + def validate_incoming(self, source: node.Node, link_type: LinkType, link_label: str) -> None: """Validate adding a link of the given type from a given node to ourself. Adding an input link to a `ProcessNode` once it is stored is illegal because this should be taken care of @@ -139,7 +141,38 @@ def validate_outgoing(self, target, link_type, link_label): super().validate_outgoing(target, link_type=link_type, link_label=link_label) -class ProcessNode(Sealable, Node): +class ProcessNodeModel(node.NodeModel, SealableModel): + process_label: Optional[str] = MetadataField( + None, + description='The process label', + ) + process_state: Optional[str] = MetadataField( + None, + description='The process state enum', + ) + process_status: Optional[str] = MetadataField( + None, + description='The process status is a generic status message', + ) + exit_status: Optional[int] = MetadataField( + None, + description='The process exit status', + ) + exit_message: Optional[str] = MetadataField( + None, + description='The process exit message', + ) + exception: Optional[str] = MetadataField( + None, + description='The process exception message', + ) + paused: Optional[bool] = MetadataField( + None, + description='Whether the process is paused', + ) + + +class ProcessNode(Sealable, node.Node): """Base class for all nodes representing the execution of a process This class and its subclasses serve as proxies in the database, for actual `Process` instances being run. The @@ -149,6 +182,8 @@ class ProcessNode(Sealable, Node): provenance graph, after the execution has terminated. """ + Model = ProcessNodeModel + _CLS_NODE_LINKS = ProcessNodeLinks _CLS_NODE_CACHING = ProcessNodeCaching @@ -188,15 +223,6 @@ def _updatable_attributes(cls) -> Tuple[str, ...]: # noqa: N805 cls.PROCESS_STATUS_KEY, ) - class Model(Node.Model, Sealable.Model): - process_label: Optional[str] = MetadataField(description='The process label') - process_state: Optional[str] = MetadataField(description='The process state enum') - process_status: Optional[str] = MetadataField(description='The process status is a generic status message') - exit_status: Optional[int] = MetadataField(description='The process exit status') - exit_message: Optional[str] = MetadataField(description='The process exit message') - exception: Optional[str] = MetadataField(description='The process exception message') - paused: bool = MetadataField(description='Whether the process is paused') - def set_metadata_inputs(self, value: Dict[str, Any]) -> None: """Set the mapping of inputs corresponding to ``metadata`` ports that were passed to the process.""" return self.base.attributes.set(self.METADATA_INPUTS_KEY, value) @@ -237,7 +263,7 @@ def recursive_merge(cls, left: dict[Any, Any], right: dict[Any, Any]) -> None: else: left[key] = value - def get_builder_restart(self) -> 'ProcessBuilder': + def get_builder_restart(self) -> ProcessBuilder: """Return a `ProcessBuilder` that is ready to relaunch the process that created this node. The process class will be set based on the `process_type` of this node and the inputs of the builder will be @@ -253,7 +279,7 @@ def get_builder_restart(self) -> 'ProcessBuilder': return builder @property - def process_class(self) -> Type['Process']: + def process_class(self) -> Type[Process]: """Return the process class that was used to create this node. :return: `Process` class @@ -437,7 +463,7 @@ def is_failed(self) -> bool: return self.is_finished and self.exit_status != 0 @property - def exit_code(self) -> Optional['ExitCode']: + def exit_code(self) -> Optional[ExitCode]: """Return the exit code of the process. It is reconstituted from the ``exit_status`` and ``exit_message`` attributes if both of those are defined. @@ -462,10 +488,10 @@ def exit_status(self) -> Optional[int]: """ return self.base.attributes.get(self.EXIT_STATUS_KEY, None) - def set_exit_status(self, status: Union[None, enum.Enum, int]) -> None: + def set_exit_status(self, status: Optional[Union[enum.Enum, int]] = None) -> None: """Set the exit status of the process - :param state: an integer exit code or None, which will be interpreted as zero + :param status: the exit status, an integer exit code, or None """ if status is None: status = 0 @@ -572,7 +598,7 @@ def unpause(self) -> None: pass @property - def called(self) -> List['ProcessNode']: + def called(self) -> List[ProcessNode]: """Return a list of nodes that the process called :returns: list of process nodes called by this process @@ -580,7 +606,7 @@ def called(self) -> List['ProcessNode']: return self.base.links.get_outgoing(link_type=(LinkType.CALL_CALC, LinkType.CALL_WORK)).all_nodes() @property - def called_descendants(self) -> List['ProcessNode']: + def called_descendants(self) -> List[ProcessNode]: """Return a list of all nodes that have been called downstream of this process This will recursively find all the called processes for this process and its children. @@ -594,7 +620,7 @@ def called_descendants(self) -> List['ProcessNode']: return descendants @property - def caller(self) -> Optional['ProcessNode']: + def caller(self) -> Optional[ProcessNode]: """Return the process node that called this process node, or None if it does not have a caller :returns: process node that called this process node instance or None diff --git a/src/aiida/orm/nodes/repository.py b/src/aiida/orm/nodes/repository.py index 5694a0f67d..c9b07a5f2a 100644 --- a/src/aiida/orm/nodes/repository.py +++ b/src/aiida/orm/nodes/repository.py @@ -9,6 +9,7 @@ import shutil import tempfile import typing as t +import zipfile from aiida.common import exceptions from aiida.manage import get_config_option @@ -273,6 +274,37 @@ def get_object_content(self, path: str, mode: t.Literal['r', 'rb'] = 'r') -> str return self._repository.get_object_content(path) + def get_object_size(self, path: str) -> int: + """Return the size of the object located at the given path. + + :param path: the relative path of the object within the repository. + :return: the size of the object in bytes. + :raises TypeError: if the path is not a string and relative path. + :raises FileNotFoundError: if the file does not exist. + :raises IsADirectoryError: if the object is a directory and not a file. + :raises OSError: if the file could not be opened. + """ + with self.open(path, mode='rb') as handle: + handle.seek(0, io.SEEK_END) + size = handle.tell() + return size + + def get_zipped_objects(self, compression: int = zipfile.ZIP_DEFLATED) -> bytes: + """Return the zipped content of the repository or a sub path within it. + + :param compression: the compression method to use. Defaults to `zipfile.ZIP_DEFLATED` (8). + :return: the zipped content as bytes. + """ + + zip_bytes_io = io.BytesIO() + + with zipfile.ZipFile(zip_bytes_io, mode='w', compression=compression) as zip_file: + for object_name in self.list_object_names(): + file_content = self.get_object_content(object_name, mode='rb') + zip_file.writestr(object_name, file_content) + + return zip_bytes_io.getvalue() + def put_object_from_bytes(self, content: bytes, path: str) -> None: """Store the given content in the repository at the given path. diff --git a/src/aiida/orm/users.py b/src/aiida/orm/users.py index bb091b9fa0..979889eeb5 100644 --- a/src/aiida/orm/users.py +++ b/src/aiida/orm/users.py @@ -8,6 +8,8 @@ ########################################################################### """Module for the ORM user class.""" +from __future__ import annotations + from typing import TYPE_CHECKING, Optional, Tuple, Type from aiida.common import exceptions @@ -23,14 +25,14 @@ __all__ = ('User',) -class UserCollection(entities.Collection['User']): +class UserCollection(entities.EntityCollection['User']): """The collection of users stored in a backend.""" @staticmethod - def _entity_base_cls() -> Type['User']: + def _entity_base_cls() -> Type[User]: return User - def get_or_create(self, email: str, **kwargs) -> Tuple[bool, 'User']: + def get_or_create(self, email: str, **kwargs) -> Tuple[bool, User]: """Get the existing user with a given email address or create an unstored one :param kwargs: The properties of the user to get or create @@ -43,21 +45,39 @@ def get_or_create(self, email: str, **kwargs) -> Tuple[bool, 'User']: except exceptions.NotExistent: return True, User(backend=self.backend, email=email, **kwargs) - def get_default(self) -> Optional['User']: + def get_default(self) -> Optional[User]: """Get the current default user""" return self.backend.default_user -class User(entities.Entity['BackendUser', UserCollection]): +class UserModel(entities.EntityModel): + email: str = MetadataField( + description='The user email', + is_attribute=False, + ) + first_name: str = MetadataField( + '', + description='The user first name', + is_attribute=False, + ) + last_name: str = MetadataField( + '', + description='The user last name', + is_attribute=False, + ) + institution: str = MetadataField( + '', + description='The user institution', + is_attribute=False, + ) + + +class User(entities.Entity['BackendUser', UserCollection, UserModel]): """AiiDA User""" - _CLS_COLLECTION = UserCollection + Model = UserModel - class Model(entities.Entity.Model): - email: str = MetadataField(description='The user email', is_attribute=False) - first_name: str = MetadataField(description='The user first name', is_attribute=False) - last_name: str = MetadataField(description='The user last name', is_attribute=False) - institution: str = MetadataField(description='The user institution', is_attribute=False) + _CLS_COLLECTION = UserCollection def __init__( self, diff --git a/src/aiida/orm/utils/mixins.py b/src/aiida/orm/utils/mixins.py index 4d8379079a..15d0ce4248 100644 --- a/src/aiida/orm/utils/mixins.py +++ b/src/aiida/orm/utils/mixins.py @@ -178,13 +178,16 @@ def get_source_code_function(self) -> str | None: return '\n'.join(content_list[start_line - 1 : end_line]) +class SealableModel(pydantic.BaseModel, defer_build=True): + sealed: bool = MetadataField(description='Whether the node is sealed') + + class Sealable: """Mixin to mark a Node as `sealable`.""" - SEALED_KEY = 'sealed' + Model = SealableModel - class Model(pydantic.BaseModel, defer_build=True): - sealed: bool = MetadataField(description='Whether the node is sealed') + SEALED_KEY = 'sealed' @classproperty def _updatable_attributes(cls) -> tuple[str, ...]: # noqa: N805 diff --git a/src/aiida/storage/psql_dos/orm/querybuilder/main.py b/src/aiida/storage/psql_dos/orm/querybuilder/main.py index 70e18620b7..8167ef4a88 100644 --- a/src/aiida/storage/psql_dos/orm/querybuilder/main.py +++ b/src/aiida/storage/psql_dos/orm/querybuilder/main.py @@ -60,6 +60,9 @@ 'computer_pk': 'dbcomputer_id', 'user_pk': 'user_id', }, + 'db_dbuser': { + 'pk': 'id', + }, 'db_dbcomputer': { 'pk': 'id', }, diff --git a/src/aiida/tools/graph/age_entities.py b/src/aiida/tools/graph/age_entities.py index cad3a6f052..d13c120305 100644 --- a/src/aiida/tools/graph/age_entities.py +++ b/src/aiida/tools/graph/age_entities.py @@ -135,7 +135,7 @@ def __len__(self) -> int: return len(self.keyset) def __repr__(self) -> str: - return f"{{{','.join(map(str, self.keyset))}}}" + return f'{{{",".join(map(str, self.keyset))}}}' def __eq__(self, other: Any) -> Any: return self.keyset == other.keyset diff --git a/tests/orm/data/code/test_abstract.py b/tests/orm/data/code/test_abstract.py index 791595c9f4..d4008156c0 100644 --- a/tests/orm/data/code/test_abstract.py +++ b/tests/orm/data/code/test_abstract.py @@ -72,4 +72,4 @@ def test_serialization(): label = 'some-label' code = MockCode(label=label) - MockCode.from_serialized(**code.serialize()) + MockCode.from_serialized(code.serialize()) diff --git a/tests/orm/data/code/test_installed.py b/tests/orm/data/code/test_installed.py index eb474bfd1b..a110b84def 100644 --- a/tests/orm/data/code/test_installed.py +++ b/tests/orm/data/code/test_installed.py @@ -152,4 +152,4 @@ def test_serialization(aiida_localhost, bash_path): """Test the deprecated :meth:`aiida.orm.nodes.data.code.installed.InstalledCode.get_execname` method.""" code = InstalledCode(label='some-label', computer=aiida_localhost, filepath_executable=str(bash_path.absolute())) - InstalledCode.from_serialized(**code.serialize()) + InstalledCode.from_serialized(code.serialize()) diff --git a/tests/orm/data/code/test_portable.py b/tests/orm/data/code/test_portable.py index 455804773e..e8d3634997 100644 --- a/tests/orm/data/code/test_portable.py +++ b/tests/orm/data/code/test_portable.py @@ -176,4 +176,4 @@ def test_serialization(tmp_path, chdir_tmp_path): (filepath_files / 'subdir').mkdir() (filepath_files / 'subdir/test').write_text('test') code = PortableCode(label='some-label', filepath_executable='bash', filepath_files=filepath_files) - PortableCode.from_serialized(**code.serialize()) + PortableCode.from_serialized(code.serialize()) diff --git a/tests/orm/models/test_models.py b/tests/orm/models/test_models.py index 7209a8fdaf..1da8314036 100644 --- a/tests/orm/models/test_models.py +++ b/tests/orm/models/test_models.py @@ -140,7 +140,14 @@ def required_arguments(request, default_user, aiida_localhost, tmp_path): if request.param is Str: return Str, {'value': 'string'} if request.param is StructureData: - return StructureData, {'cell': [[1, 0, 0], [0, 1, 0], [0, 0, 1]]} + return StructureData, { + 'cell': [[1, 0, 0], [0, 1, 0], [0, 0, 1]], + 'pbc1': True, + 'pbc2': True, + 'pbc3': True, + 'sites': [{'kind_name': 'H', 'position': (0.0, 0.0, 0.0)}], + 'kinds': [{'name': 'H', 'mass': 1.0, 'symbols': ('H',), 'weights': (1.0,)}], + } if request.param is RemoteData: return RemoteData, {'remote_path': '/some/path'} if request.param is RemoteStashData: @@ -169,18 +176,18 @@ def test_roundtrip(required_arguments, tmp_path): assert isinstance(entity, cls) # Get the model instance from the entity instance - model = entity._to_model(tmp_path) + model = entity.to_model(repository_path=tmp_path) assert isinstance(model, BaseModel) # Reconstruct the entity instance from the model instance - roundtrip = cls._from_model(model) + roundtrip = cls.from_model(model) assert isinstance(roundtrip, cls) # Get the model instance again from the reconstructed entity and check that the fields that would be passed to the # ORM entity constructor are identical of the original model. The ``model_to_orm_field_values`` excludes values of # fields that define ``exclude_to_orm=True`` because these can change during roundtrips. This because these # typically correspond to entity fields that have defaults set on the database level, e.g., UUIDs. - roundtrip_model = roundtrip._to_model(tmp_path) + roundtrip_model = roundtrip.to_model(repository_path=tmp_path) original_field_values = cls.model_to_orm_field_values(model) for key, value in cls.model_to_orm_field_values(roundtrip_model).items(): @@ -206,5 +213,5 @@ def test_roundtrip_serialization(required_arguments, tmp_path): assert isinstance(entity, cls) # Get the model instance from the entity instance - serialized_entity = entity.serialize(tmp_path) - entity.from_serialized(**serialized_entity) + serialized_entity = entity.serialize(repository_path=tmp_path, mode='python') + entity.from_serialized(serialized_entity) diff --git a/tests/orm/test_fields.py b/tests/orm/test_fields.py index f22fdc3c73..38d60547db 100644 --- a/tests/orm/test_fields.py +++ b/tests/orm/test_fields.py @@ -53,11 +53,13 @@ def test_all_node_fields(node_and_data_entry_points: list[tuple[str, str]], data def test_add_field(): """Test the `add_field` API.""" + class NewNodeModel(orm.nodes.data.data.DataModel): + key1: str = MetadataField( # type: ignore[annotation-unchecked] + is_subscriptable=False, + ) + class NewNode(orm.Data): - class Model(orm.Data.Model): - key1: str = MetadataField( # type: ignore[annotation-unchecked] - is_subscriptable=False, - ) + Model = NewNodeModel node = NewNode() @@ -101,10 +103,12 @@ def _dummy(*args, **kwargs): _dummy, ) + class NewNodeModel(orm.nodes.data.data.DataModel): + some_label: str = MetadataField() # type: ignore[annotation-unchecked] + some_value: int = MetadataField() # type: ignore[annotation-unchecked] + class NewNode(orm.Data): - class Model(orm.Data.Model): - some_label: str = MetadataField() # type: ignore[annotation-unchecked] - some_value: int = MetadataField() # type: ignore[annotation-unchecked] + Model = NewNodeModel node = NewNode() node.base.attributes.set_many({'some_label': 'A', 'some_value': 1}) diff --git a/tests/orm/test_fields/fields_AuthInfo.yml b/tests/orm/test_fields/fields_AuthInfo.yml index 505c96da91..91ba1e63f6 100644 --- a/tests/orm/test_fields/fields_AuthInfo.yml +++ b/tests/orm/test_fields/fields_AuthInfo.yml @@ -2,5 +2,5 @@ auth_params: QbDictField('auth_params', dtype=typing.Dict[str, typing.Any], is_a computer: QbNumericField('computer', dtype=, is_attribute=False) enabled: QbField('enabled', dtype=, is_attribute=False) metadata: QbDictField('metadata', dtype=typing.Dict[str, typing.Any], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) user: QbNumericField('user', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_Comment.yml b/tests/orm/test_fields/fields_Comment.yml index 30aedcef79..f227b00247 100644 --- a/tests/orm/test_fields/fields_Comment.yml +++ b/tests/orm/test_fields/fields_Comment.yml @@ -1,7 +1,7 @@ content: QbStrField('content', dtype=, is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) node: QbNumericField('node', dtype=, is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) user: QbNumericField('user', dtype=, is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_Computer.yml b/tests/orm/test_fields/fields_Computer.yml index 7d4e37168b..fc980dec16 100644 --- a/tests/orm/test_fields/fields_Computer.yml +++ b/tests/orm/test_fields/fields_Computer.yml @@ -2,7 +2,7 @@ description: QbStrField('description', dtype=, is_attribute=False) hostname: QbStrField('hostname', dtype=, is_attribute=False) label: QbStrField('label', dtype=, is_attribute=False) metadata: QbDictField('metadata', dtype=typing.Dict[str, typing.Any], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) scheduler_type: QbStrField('scheduler_type', dtype=, is_attribute=False) transport_type: QbStrField('transport_type', dtype=, is_attribute=False) -uuid: QbStrField('uuid', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_Group.yml b/tests/orm/test_fields/fields_Group.yml index 537c76a11d..1844039c7e 100644 --- a/tests/orm/test_fields/fields_Group.yml +++ b/tests/orm/test_fields/fields_Group.yml @@ -1,9 +1,9 @@ -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) label: QbStrField('label', dtype=, is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) -time: QbNumericField('time', dtype=typing.Optional[datetime.datetime], is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) +time: QbNumericField('time', dtype=, is_attribute=False) type_string: QbStrField('type_string', dtype=, is_attribute=False) user: QbNumericField('user', dtype=, is_attribute=False) -uuid: QbStrField('uuid', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_Log.yml b/tests/orm/test_fields/fields_Log.yml index 90bf0a5b0b..0cacd5ac55 100644 --- a/tests/orm/test_fields/fields_Log.yml +++ b/tests/orm/test_fields/fields_Log.yml @@ -3,6 +3,6 @@ levelname: QbStrField('levelname', dtype=, is_attribute=False) loggername: QbStrField('loggername', dtype=, is_attribute=False) message: QbStrField('message', dtype=, is_attribute=False) metadata: QbDictField('metadata', dtype=typing.Dict[str, typing.Any], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) time: QbNumericField('time', dtype=, is_attribute=False) -uuid: QbStrField('uuid', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_User.yml b/tests/orm/test_fields/fields_User.yml index 06fb9cd05c..247accb431 100644 --- a/tests/orm/test_fields/fields_User.yml +++ b/tests/orm/test_fields/fields_User.yml @@ -2,4 +2,4 @@ email: QbStrField('email', dtype=, is_attribute=False) first_name: QbStrField('first_name', dtype=, is_attribute=False) institution: QbStrField('institution', dtype=, is_attribute=False) last_name: QbStrField('last_name', dtype=, is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.data.core.array.ArrayData.yml b/tests/orm/test_fields/fields_aiida.data.core.array.ArrayData.yml index c31304e6f5..0086bc2b59 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.array.ArrayData.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.array.ArrayData.yml @@ -1,20 +1,19 @@ arrays: QbDictField('arrays', dtype=typing.Optional[dict[str, bytes]], is_attribute=True) -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +label: QbStrField('label', dtype=, is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.data.core.array.bands.BandsData.yml b/tests/orm/test_fields/fields_aiida.data.core.array.bands.BandsData.yml index f0bca48c81..a4d635a7bd 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.array.bands.BandsData.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.array.bands.BandsData.yml @@ -1,31 +1,32 @@ array_labels: QbArrayField('array_labels', dtype=typing.Optional[typing.List[str]], is_attribute=True) arrays: QbDictField('arrays', dtype=typing.Optional[dict[str, bytes]], is_attribute=True) -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -cell: QbArrayField('cell', dtype=typing.List[typing.List[float]], is_attribute=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -label_numbers: QbArrayField('label_numbers', dtype=typing.List[int], is_attribute=True) -labels: QbArrayField('labels', dtype=typing.List[str], is_attribute=True) -mesh: QbArrayField('mesh', dtype=typing.List[int], is_attribute=True) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -offset: QbArrayField('offset', dtype=typing.List[float], is_attribute=True) -pbc1: QbField('pbc1', dtype=, is_attribute=True) -pbc2: QbField('pbc2', dtype=, is_attribute=True) -pbc3: QbField('pbc3', dtype=, is_attribute=True) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +cell: QbArrayField('cell', dtype=typing.Optional[typing.List[typing.List[float]]], + is_attribute=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +label: QbStrField('label', dtype=, is_attribute=False) +label_numbers: QbArrayField('label_numbers', dtype=typing.Optional[typing.List[int]], + is_attribute=True) +labels: QbArrayField('labels', dtype=typing.Optional[typing.List[str]], is_attribute=True) +mesh: QbArrayField('mesh', dtype=typing.Optional[typing.List[int]], is_attribute=True) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +offset: QbArrayField('offset', dtype=typing.Optional[typing.List[float]], is_attribute=True) +pbc1: QbField('pbc1', dtype=typing.Optional[bool], is_attribute=True) +pbc2: QbField('pbc2', dtype=typing.Optional[bool], is_attribute=True) +pbc3: QbField('pbc3', dtype=typing.Optional[bool], is_attribute=True) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) -units: QbStrField('units', dtype=, is_attribute=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +units: QbStrField('units', dtype=typing.Optional[str], is_attribute=True) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.data.core.array.kpoints.KpointsData.yml b/tests/orm/test_fields/fields_aiida.data.core.array.kpoints.KpointsData.yml index 6d0aaa2b6f..e0a0e7533a 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.array.kpoints.KpointsData.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.array.kpoints.KpointsData.yml @@ -1,28 +1,29 @@ arrays: QbDictField('arrays', dtype=typing.Optional[dict[str, bytes]], is_attribute=True) -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -cell: QbArrayField('cell', dtype=typing.List[typing.List[float]], is_attribute=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -label_numbers: QbArrayField('label_numbers', dtype=typing.List[int], is_attribute=True) -labels: QbArrayField('labels', dtype=typing.List[str], is_attribute=True) -mesh: QbArrayField('mesh', dtype=typing.List[int], is_attribute=True) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -offset: QbArrayField('offset', dtype=typing.List[float], is_attribute=True) -pbc1: QbField('pbc1', dtype=, is_attribute=True) -pbc2: QbField('pbc2', dtype=, is_attribute=True) -pbc3: QbField('pbc3', dtype=, is_attribute=True) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +cell: QbArrayField('cell', dtype=typing.Optional[typing.List[typing.List[float]]], + is_attribute=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +label: QbStrField('label', dtype=, is_attribute=False) +label_numbers: QbArrayField('label_numbers', dtype=typing.Optional[typing.List[int]], + is_attribute=True) +labels: QbArrayField('labels', dtype=typing.Optional[typing.List[str]], is_attribute=True) +mesh: QbArrayField('mesh', dtype=typing.Optional[typing.List[int]], is_attribute=True) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +offset: QbArrayField('offset', dtype=typing.Optional[typing.List[float]], is_attribute=True) +pbc1: QbField('pbc1', dtype=typing.Optional[bool], is_attribute=True) +pbc2: QbField('pbc2', dtype=typing.Optional[bool], is_attribute=True) +pbc3: QbField('pbc3', dtype=typing.Optional[bool], is_attribute=True) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.data.core.array.projection.ProjectionData.yml b/tests/orm/test_fields/fields_aiida.data.core.array.projection.ProjectionData.yml index c31304e6f5..0086bc2b59 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.array.projection.ProjectionData.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.array.projection.ProjectionData.yml @@ -1,20 +1,19 @@ arrays: QbDictField('arrays', dtype=typing.Optional[dict[str, bytes]], is_attribute=True) -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +label: QbStrField('label', dtype=, is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.data.core.array.trajectory.TrajectoryData.yml b/tests/orm/test_fields/fields_aiida.data.core.array.trajectory.TrajectoryData.yml index 87aaa30148..0fa7353e3f 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.array.trajectory.TrajectoryData.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.array.trajectory.TrajectoryData.yml @@ -1,23 +1,22 @@ arrays: QbDictField('arrays', dtype=typing.Optional[dict[str, bytes]], is_attribute=True) -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +label: QbStrField('label', dtype=, is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) symbols: QbArrayField('symbols', dtype=typing.List[str], is_attribute=True) -units_positions: QbStrField('units_positions', dtype=, is_attribute=True) -units_times: QbStrField('units_times', dtype=, is_attribute=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +units_positions: QbStrField('units_positions', dtype=typing.Optional[str], is_attribute=True) +units_times: QbStrField('units_times', dtype=typing.Optional[str], is_attribute=True) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.data.core.array.xy.XyData.yml b/tests/orm/test_fields/fields_aiida.data.core.array.xy.XyData.yml index c31304e6f5..0086bc2b59 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.array.xy.XyData.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.array.xy.XyData.yml @@ -1,20 +1,19 @@ arrays: QbDictField('arrays', dtype=typing.Optional[dict[str, bytes]], is_attribute=True) -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +label: QbStrField('label', dtype=, is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.data.core.base.BaseType.yml b/tests/orm/test_fields/fields_aiida.data.core.base.BaseType.yml index 457621f596..ac20a817c7 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.base.BaseType.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.base.BaseType.yml @@ -1,20 +1,19 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +label: QbStrField('label', dtype=, is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) value: QbField('value', dtype=typing.Any, is_attribute=True) diff --git a/tests/orm/test_fields/fields_aiida.data.core.bool.Bool.yml b/tests/orm/test_fields/fields_aiida.data.core.bool.Bool.yml index 457621f596..e01e9f21ed 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.bool.Bool.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.bool.Bool.yml @@ -1,20 +1,19 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +label: QbStrField('label', dtype=, is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) -value: QbField('value', dtype=typing.Any, is_attribute=True) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) +value: QbField('value', dtype=, is_attribute=True) diff --git a/tests/orm/test_fields/fields_aiida.data.core.cif.CifData.yml b/tests/orm/test_fields/fields_aiida.data.core.cif.CifData.yml index da4e1d40d9..6d1b4bc1a9 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.cif.CifData.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.cif.CifData.yml @@ -1,25 +1,24 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) content: QbField('content', dtype=, is_attribute=True) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -filename: QbStrField('filename', dtype=typing.Optional[str], is_attribute=True) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +filename: QbStrField('filename', dtype=, is_attribute=True) formulae: QbArrayField('formulae', dtype=typing.Optional[typing.List[str]], is_attribute=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) +label: QbStrField('label', dtype=, is_attribute=False) md5: QbStrField('md5', dtype=typing.Optional[str], is_attribute=True) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) spacegroup_numbers: QbArrayField('spacegroup_numbers', dtype=typing.Optional[typing.List[str]], is_attribute=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.data.core.code.Code.yml b/tests/orm/test_fields/fields_aiida.data.core.code.Code.yml index 8ebaa0804d..df9f20e1fc 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.code.Code.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.code.Code.yml @@ -1,29 +1,28 @@ append_text: QbStrField('append_text', dtype=, is_attribute=True) -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) default_calc_job_plugin: QbStrField('default_calc_job_plugin', dtype=typing.Optional[str], is_attribute=True) description: QbStrField('description', dtype=, is_attribute=True) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) input_plugin: QbStrField('input_plugin', dtype=typing.Optional[str], is_attribute=True) is_local: QbField('is_local', dtype=typing.Optional[bool], is_attribute=True) label: QbStrField('label', dtype=, is_attribute=True) local_executable: QbStrField('local_executable', dtype=typing.Optional[str], is_attribute=True) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) prepend_text: QbStrField('prepend_text', dtype=, is_attribute=True) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) remote_exec_path: QbStrField('remote_exec_path', dtype=typing.Optional[str], is_attribute=True) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) use_double_quotes: QbField('use_double_quotes', dtype=, is_attribute=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) with_mpi: QbField('with_mpi', dtype=typing.Optional[bool], is_attribute=True) diff --git a/tests/orm/test_fields/fields_aiida.data.core.code.abstract.AbstractCode.yml b/tests/orm/test_fields/fields_aiida.data.core.code.abstract.AbstractCode.yml index 4dc178d9fc..ee8bb7beef 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.code.abstract.AbstractCode.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.code.abstract.AbstractCode.yml @@ -1,25 +1,24 @@ append_text: QbStrField('append_text', dtype=, is_attribute=True) -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) default_calc_job_plugin: QbStrField('default_calc_job_plugin', dtype=typing.Optional[str], is_attribute=True) description: QbStrField('description', dtype=, is_attribute=True) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) label: QbStrField('label', dtype=, is_attribute=True) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) prepend_text: QbStrField('prepend_text', dtype=, is_attribute=True) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) use_double_quotes: QbField('use_double_quotes', dtype=, is_attribute=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) with_mpi: QbField('with_mpi', dtype=typing.Optional[bool], is_attribute=True) diff --git a/tests/orm/test_fields/fields_aiida.data.core.code.containerized.ContainerizedCode.yml b/tests/orm/test_fields/fields_aiida.data.core.code.containerized.ContainerizedCode.yml index e7f12a1a94..4511495ff7 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.code.containerized.ContainerizedCode.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.code.containerized.ContainerizedCode.yml @@ -1,29 +1,28 @@ append_text: QbStrField('append_text', dtype=, is_attribute=True) -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbStrField('computer', dtype=, is_attribute=True) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=, is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) default_calc_job_plugin: QbStrField('default_calc_job_plugin', dtype=typing.Optional[str], is_attribute=True) description: QbStrField('description', dtype=, is_attribute=True) engine_command: QbStrField('engine_command', dtype=, is_attribute=True) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) filepath_executable: QbStrField('filepath_executable', dtype=, is_attribute=True) image_name: QbStrField('image_name', dtype=, is_attribute=True) label: QbStrField('label', dtype=, is_attribute=True) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) prepend_text: QbStrField('prepend_text', dtype=, is_attribute=True) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) use_double_quotes: QbField('use_double_quotes', dtype=, is_attribute=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) with_mpi: QbField('with_mpi', dtype=typing.Optional[bool], is_attribute=True) wrap_cmdline_params: QbField('wrap_cmdline_params', dtype=, is_attribute=True) diff --git a/tests/orm/test_fields/fields_aiida.data.core.code.installed.InstalledCode.yml b/tests/orm/test_fields/fields_aiida.data.core.code.installed.InstalledCode.yml index 15089b4a3d..6bdfcaed89 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.code.installed.InstalledCode.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.code.installed.InstalledCode.yml @@ -1,26 +1,25 @@ append_text: QbStrField('append_text', dtype=, is_attribute=True) -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbStrField('computer', dtype=, is_attribute=True) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=, is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) default_calc_job_plugin: QbStrField('default_calc_job_plugin', dtype=typing.Optional[str], is_attribute=True) description: QbStrField('description', dtype=, is_attribute=True) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) filepath_executable: QbStrField('filepath_executable', dtype=, is_attribute=True) label: QbStrField('label', dtype=, is_attribute=True) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) prepend_text: QbStrField('prepend_text', dtype=, is_attribute=True) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) use_double_quotes: QbField('use_double_quotes', dtype=, is_attribute=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) with_mpi: QbField('with_mpi', dtype=typing.Optional[bool], is_attribute=True) diff --git a/tests/orm/test_fields/fields_aiida.data.core.code.portable.PortableCode.yml b/tests/orm/test_fields/fields_aiida.data.core.code.portable.PortableCode.yml index b874b26466..6d69c90cda 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.code.portable.PortableCode.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.code.portable.PortableCode.yml @@ -1,27 +1,26 @@ append_text: QbStrField('append_text', dtype=, is_attribute=True) -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) default_calc_job_plugin: QbStrField('default_calc_job_plugin', dtype=typing.Optional[str], is_attribute=True) description: QbStrField('description', dtype=, is_attribute=True) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) filepath_executable: QbStrField('filepath_executable', dtype=, is_attribute=True) filepath_files: QbStrField('filepath_files', dtype=, is_attribute=False) label: QbStrField('label', dtype=, is_attribute=True) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) prepend_text: QbStrField('prepend_text', dtype=, is_attribute=True) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) use_double_quotes: QbField('use_double_quotes', dtype=, is_attribute=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) with_mpi: QbField('with_mpi', dtype=typing.Optional[bool], is_attribute=True) diff --git a/tests/orm/test_fields/fields_aiida.data.core.dict.Dict.yml b/tests/orm/test_fields/fields_aiida.data.core.dict.Dict.yml index 710d253b4d..95d389477e 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.dict.Dict.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.dict.Dict.yml @@ -1,21 +1,20 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +label: QbStrField('label', dtype=, is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) value: QbDictField('value', dtype=typing.Dict[str, typing.Any], is_attribute=False, is_subscriptable=True) diff --git a/tests/orm/test_fields/fields_aiida.data.core.enum.EnumData.yml b/tests/orm/test_fields/fields_aiida.data.core.enum.EnumData.yml index cfc3976079..8df237bcef 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.enum.EnumData.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.enum.EnumData.yml @@ -1,20 +1,19 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +label: QbStrField('label', dtype=, is_attribute=False) member: QbField('member', dtype=, is_attribute=True) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.data.core.float.Float.yml b/tests/orm/test_fields/fields_aiida.data.core.float.Float.yml index 457621f596..6c891b8fa0 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.float.Float.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.float.Float.yml @@ -1,20 +1,19 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +label: QbStrField('label', dtype=, is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) -value: QbField('value', dtype=typing.Any, is_attribute=True) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) +value: QbNumericField('value', dtype=, is_attribute=True) diff --git a/tests/orm/test_fields/fields_aiida.data.core.folder.FolderData.yml b/tests/orm/test_fields/fields_aiida.data.core.folder.FolderData.yml index 5bee2ef441..07acd8a330 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.folder.FolderData.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.folder.FolderData.yml @@ -1,19 +1,18 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +label: QbStrField('label', dtype=, is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.data.core.int.Int.yml b/tests/orm/test_fields/fields_aiida.data.core.int.Int.yml index 457621f596..0e34cfd29a 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.int.Int.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.int.Int.yml @@ -1,20 +1,19 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +label: QbStrField('label', dtype=, is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) -value: QbField('value', dtype=typing.Any, is_attribute=True) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) +value: QbNumericField('value', dtype=, is_attribute=True) diff --git a/tests/orm/test_fields/fields_aiida.data.core.jsonable.JsonableData.yml b/tests/orm/test_fields/fields_aiida.data.core.jsonable.JsonableData.yml index 1166fbc570..3ea41ba032 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.jsonable.JsonableData.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.jsonable.JsonableData.yml @@ -1,21 +1,20 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +label: QbStrField('label', dtype=, is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) obj: QbField('obj', dtype=, is_attribute=True) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.data.core.list.List.yml b/tests/orm/test_fields/fields_aiida.data.core.list.List.yml index 4edd6d3380..e91319b9d6 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.list.List.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.list.List.yml @@ -1,20 +1,19 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +label: QbStrField('label', dtype=, is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) value: QbArrayField('value', dtype=typing.List[typing.Any], is_attribute=True) diff --git a/tests/orm/test_fields/fields_aiida.data.core.numeric.NumericType.yml b/tests/orm/test_fields/fields_aiida.data.core.numeric.NumericType.yml index 457621f596..ac20a817c7 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.numeric.NumericType.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.numeric.NumericType.yml @@ -1,20 +1,19 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +label: QbStrField('label', dtype=, is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) value: QbField('value', dtype=typing.Any, is_attribute=True) diff --git a/tests/orm/test_fields/fields_aiida.data.core.orbital.OrbitalData.yml b/tests/orm/test_fields/fields_aiida.data.core.orbital.OrbitalData.yml index 5bee2ef441..07acd8a330 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.orbital.OrbitalData.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.orbital.OrbitalData.yml @@ -1,19 +1,18 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +label: QbStrField('label', dtype=, is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.data.core.remote.RemoteData.yml b/tests/orm/test_fields/fields_aiida.data.core.remote.RemoteData.yml index 5086780659..dae0981bc0 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.remote.RemoteData.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.remote.RemoteData.yml @@ -1,20 +1,19 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +label: QbStrField('label', dtype=, is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) remote_path: QbStrField('remote_path', dtype=typing.Optional[str], is_attribute=True) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.data.core.remote.stash.RemoteStashData.yml b/tests/orm/test_fields/fields_aiida.data.core.remote.stash.RemoteStashData.yml index 35e6e4188e..fdb6988347 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.remote.stash.RemoteStashData.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.remote.stash.RemoteStashData.yml @@ -1,20 +1,19 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +label: QbStrField('label', dtype=, is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) stash_mode: QbField('stash_mode', dtype=, is_attribute=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.data.core.remote.stash.compress.RemoteStashCompressedData.yml b/tests/orm/test_fields/fields_aiida.data.core.remote.stash.compress.RemoteStashCompressedData.yml index 40e941f933..710840981b 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.remote.stash.compress.RemoteStashCompressedData.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.remote.stash.compress.RemoteStashCompressedData.yml @@ -1,23 +1,22 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) dereference: QbField('dereference', dtype=, is_attribute=True) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +label: QbStrField('label', dtype=, is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) source_list: QbArrayField('source_list', dtype=typing.List[str], is_attribute=True) stash_mode: QbField('stash_mode', dtype=, is_attribute=True) target_basepath: QbStrField('target_basepath', dtype=, is_attribute=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.data.core.remote.stash.custom.RemoteStashCustomData.yml b/tests/orm/test_fields/fields_aiida.data.core.remote.stash.custom.RemoteStashCustomData.yml index 82e177e738..6713968e95 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.remote.stash.custom.RemoteStashCustomData.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.remote.stash.custom.RemoteStashCustomData.yml @@ -1,22 +1,21 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +label: QbStrField('label', dtype=, is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) source_list: QbArrayField('source_list', dtype=typing.List[str], is_attribute=True) stash_mode: QbField('stash_mode', dtype=, is_attribute=True) target_basepath: QbStrField('target_basepath', dtype=, is_attribute=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.data.core.remote.stash.folder.RemoteStashFolderData.yml b/tests/orm/test_fields/fields_aiida.data.core.remote.stash.folder.RemoteStashFolderData.yml index 82e177e738..6713968e95 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.remote.stash.folder.RemoteStashFolderData.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.remote.stash.folder.RemoteStashFolderData.yml @@ -1,22 +1,21 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +label: QbStrField('label', dtype=, is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) source_list: QbArrayField('source_list', dtype=typing.List[str], is_attribute=True) stash_mode: QbField('stash_mode', dtype=, is_attribute=True) target_basepath: QbStrField('target_basepath', dtype=, is_attribute=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.data.core.singlefile.SinglefileData.yml b/tests/orm/test_fields/fields_aiida.data.core.singlefile.SinglefileData.yml index 71f35c46f7..c320eb4374 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.singlefile.SinglefileData.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.singlefile.SinglefileData.yml @@ -1,21 +1,20 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) content: QbField('content', dtype=, is_attribute=True) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -filename: QbStrField('filename', dtype=typing.Optional[str], is_attribute=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +filename: QbStrField('filename', dtype=, is_attribute=True) +label: QbStrField('label', dtype=, is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.data.core.str.Str.yml b/tests/orm/test_fields/fields_aiida.data.core.str.Str.yml index 457621f596..73117515a3 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.str.Str.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.str.Str.yml @@ -1,20 +1,19 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +label: QbStrField('label', dtype=, is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) -value: QbField('value', dtype=typing.Any, is_attribute=True) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) +value: QbStrField('value', dtype=, is_attribute=True) diff --git a/tests/orm/test_fields/fields_aiida.data.core.structure.StructureData.yml b/tests/orm/test_fields/fields_aiida.data.core.structure.StructureData.yml index 8e94962d01..bf702d4905 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.structure.StructureData.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.structure.StructureData.yml @@ -1,25 +1,24 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) cell: QbArrayField('cell', dtype=typing.List[typing.List[float]], is_attribute=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -kinds: QbArrayField('kinds', dtype=typing.Optional[typing.List[dict]], is_attribute=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +kinds: QbArrayField('kinds', dtype=typing.List[dict], is_attribute=True) +label: QbStrField('label', dtype=, is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) pbc1: QbField('pbc1', dtype=, is_attribute=True) pbc2: QbField('pbc2', dtype=, is_attribute=True) pbc3: QbField('pbc3', dtype=, is_attribute=True) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) -sites: QbArrayField('sites', dtype=typing.Optional[typing.List[dict]], is_attribute=True) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) +sites: QbArrayField('sites', dtype=typing.List[dict], is_attribute=True) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.data.core.upf.UpfData.yml b/tests/orm/test_fields/fields_aiida.data.core.upf.UpfData.yml index 71f35c46f7..c320eb4374 100644 --- a/tests/orm/test_fields/fields_aiida.data.core.upf.UpfData.yml +++ b/tests/orm/test_fields/fields_aiida.data.core.upf.UpfData.yml @@ -1,21 +1,20 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) +attributes: QbDictField('attributes', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +computer: QbStrField('computer', dtype=typing.Optional[str], is_attribute=False) content: QbField('content', dtype=, is_attribute=True) -ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], - is_attribute=False, is_subscriptable=True) -filename: QbStrField('filename', dtype=typing.Optional[str], is_attribute=True) -label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) -mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) -node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) -pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) +ctime: QbNumericField('ctime', dtype=, is_attribute=False) +description: QbStrField('description', dtype=, is_attribute=False) +extras: QbDictField('extras', dtype=typing.Dict[str, typing.Any], is_attribute=False, + is_subscriptable=True) +filename: QbStrField('filename', dtype=, is_attribute=True) +label: QbStrField('label', dtype=, is_attribute=False) +mtime: QbNumericField('mtime', dtype=, is_attribute=False) +node_type: QbStrField('node_type', dtype=, is_attribute=False) +pk: QbNumericField('pk', dtype=, is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, - bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, - typing.Any]], is_attribute=False) +repository_content: QbDictField('repository_content', dtype=dict[str, bytes], is_attribute=False) +repository_metadata: QbDictField('repository_metadata', dtype=typing.Dict[str, typing.Any], + is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) -user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) -uuid: QbStrField('uuid', dtype=typing.Optional[str], is_attribute=False) +user: QbNumericField('user', dtype=, is_attribute=False) +uuid: QbField('uuid', dtype=, is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.node.data.Data.yml b/tests/orm/test_fields/fields_aiida.node.data.Data.yml index 5bee2ef441..cf63271c5e 100644 --- a/tests/orm/test_fields/fields_aiida.node.data.Data.yml +++ b/tests/orm/test_fields/fields_aiida.node.data.Data.yml @@ -1,18 +1,22 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], +attributes: + QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) +computer: QbNumericField('computer', dtype=typing.Optional[str], is_attribute=False) ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], +extras: + QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], is_attribute=False, is_subscriptable=True) label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) node_type: QbStrField('node_type', dtype=typing.Optional[str], is_attribute=False) pk: QbNumericField('pk', dtype=typing.Optional[int], is_attribute=False) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, +repository_content: + QbDictField('repository_content', dtype=typing.Optional[dict[str, bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, +repository_metadata: + QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, typing.Any]], is_attribute=False) source: QbDictField('source', dtype=typing.Optional[dict], is_attribute=True, is_subscriptable=True) user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.node.process.ProcessNode.yml b/tests/orm/test_fields/fields_aiida.node.process.ProcessNode.yml index d8928ee1a4..6639d94fba 100644 --- a/tests/orm/test_fields/fields_aiida.node.process.ProcessNode.yml +++ b/tests/orm/test_fields/fields_aiida.node.process.ProcessNode.yml @@ -1,12 +1,14 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], +attributes: + QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) +computer: QbNumericField('computer', dtype=typing.Optional[str], is_attribute=False) ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) exception: QbStrField('exception', dtype=typing.Optional[str], is_attribute=True) exit_message: QbStrField('exit_message', dtype=typing.Optional[str], is_attribute=True) exit_status: QbNumericField('exit_status', dtype=typing.Optional[int], is_attribute=True) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], +extras: + QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], is_attribute=False, is_subscriptable=True) label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) @@ -17,9 +19,11 @@ process_label: QbStrField('process_label', dtype=typing.Optional[str], is_attrib process_state: QbStrField('process_state', dtype=typing.Optional[str], is_attribute=True) process_status: QbStrField('process_status', dtype=typing.Optional[str], is_attribute=True) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, +repository_content: + QbDictField('repository_content', dtype=typing.Optional[dict[str, bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, +repository_metadata: + QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, typing.Any]], is_attribute=False) sealed: QbField('sealed', dtype=, is_attribute=True) user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.node.process.calculation.CalculationNode.yml b/tests/orm/test_fields/fields_aiida.node.process.calculation.CalculationNode.yml index d8928ee1a4..6639d94fba 100644 --- a/tests/orm/test_fields/fields_aiida.node.process.calculation.CalculationNode.yml +++ b/tests/orm/test_fields/fields_aiida.node.process.calculation.CalculationNode.yml @@ -1,12 +1,14 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], +attributes: + QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) +computer: QbNumericField('computer', dtype=typing.Optional[str], is_attribute=False) ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) exception: QbStrField('exception', dtype=typing.Optional[str], is_attribute=True) exit_message: QbStrField('exit_message', dtype=typing.Optional[str], is_attribute=True) exit_status: QbNumericField('exit_status', dtype=typing.Optional[int], is_attribute=True) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], +extras: + QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], is_attribute=False, is_subscriptable=True) label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) @@ -17,9 +19,11 @@ process_label: QbStrField('process_label', dtype=typing.Optional[str], is_attrib process_state: QbStrField('process_state', dtype=typing.Optional[str], is_attribute=True) process_status: QbStrField('process_status', dtype=typing.Optional[str], is_attribute=True) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, +repository_content: + QbDictField('repository_content', dtype=typing.Optional[dict[str, bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, +repository_metadata: + QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, typing.Any]], is_attribute=False) sealed: QbField('sealed', dtype=, is_attribute=True) user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.node.process.calculation.calcfunction.CalcFunctionNode.yml b/tests/orm/test_fields/fields_aiida.node.process.calculation.calcfunction.CalcFunctionNode.yml index d8928ee1a4..6639d94fba 100644 --- a/tests/orm/test_fields/fields_aiida.node.process.calculation.calcfunction.CalcFunctionNode.yml +++ b/tests/orm/test_fields/fields_aiida.node.process.calculation.calcfunction.CalcFunctionNode.yml @@ -1,12 +1,14 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], +attributes: + QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) +computer: QbNumericField('computer', dtype=typing.Optional[str], is_attribute=False) ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) exception: QbStrField('exception', dtype=typing.Optional[str], is_attribute=True) exit_message: QbStrField('exit_message', dtype=typing.Optional[str], is_attribute=True) exit_status: QbNumericField('exit_status', dtype=typing.Optional[int], is_attribute=True) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], +extras: + QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], is_attribute=False, is_subscriptable=True) label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) @@ -17,9 +19,11 @@ process_label: QbStrField('process_label', dtype=typing.Optional[str], is_attrib process_state: QbStrField('process_state', dtype=typing.Optional[str], is_attribute=True) process_status: QbStrField('process_status', dtype=typing.Optional[str], is_attribute=True) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, +repository_content: + QbDictField('repository_content', dtype=typing.Optional[dict[str, bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, +repository_metadata: + QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, typing.Any]], is_attribute=False) sealed: QbField('sealed', dtype=, is_attribute=True) user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.node.process.calculation.calcjob.CalcJobNode.yml b/tests/orm/test_fields/fields_aiida.node.process.calculation.calcjob.CalcJobNode.yml index 8da4b34cb8..fc0b31b968 100644 --- a/tests/orm/test_fields/fields_aiida.node.process.calculation.calcjob.CalcJobNode.yml +++ b/tests/orm/test_fields/fields_aiida.node.process.calculation.calcjob.CalcJobNode.yml @@ -1,13 +1,15 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], +attributes: + QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) +computer: QbNumericField('computer', dtype=typing.Optional[str], is_attribute=False) ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) detailed_job_info: QbDictField('detailed_job_info', dtype=typing.Optional[dict], is_attribute=True) exception: QbStrField('exception', dtype=typing.Optional[str], is_attribute=True) exit_message: QbStrField('exit_message', dtype=typing.Optional[str], is_attribute=True) exit_status: QbNumericField('exit_status', dtype=typing.Optional[int], is_attribute=True) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], +extras: + QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], is_attribute=False, is_subscriptable=True) imported: QbField('imported', dtype=typing.Optional[bool], is_attribute=True) job_id: QbStrField('job_id', dtype=typing.Optional[str], is_attribute=True) @@ -22,15 +24,20 @@ process_state: QbStrField('process_state', dtype=typing.Optional[str], is_attrib process_status: QbStrField('process_status', dtype=typing.Optional[str], is_attribute=True) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) remote_workdir: QbStrField('remote_workdir', dtype=typing.Optional[str], is_attribute=True) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, +repository_content: + QbDictField('repository_content', dtype=typing.Optional[dict[str, bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, +repository_metadata: + QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, typing.Any]], is_attribute=False) -retrieve_list: QbArrayField('retrieve_list', dtype=typing.Optional[typing.List[str]], +retrieve_list: + QbArrayField('retrieve_list', dtype=typing.Optional[typing.List[str]], is_attribute=True) -retrieve_temporary_list: QbArrayField('retrieve_temporary_list', dtype=typing.Optional[typing.List[str]], +retrieve_temporary_list: + QbArrayField('retrieve_temporary_list', dtype=typing.Optional[typing.List[str]], is_attribute=True) -scheduler_lastchecktime: QbStrField('scheduler_lastchecktime', dtype=typing.Optional[str], +scheduler_lastchecktime: + QbStrField('scheduler_lastchecktime', dtype=typing.Optional[str], is_attribute=True) scheduler_state: QbStrField('scheduler_state', dtype=typing.Optional[str], is_attribute=True) sealed: QbField('sealed', dtype=, is_attribute=True) diff --git a/tests/orm/test_fields/fields_aiida.node.process.workflow.WorkflowNode.yml b/tests/orm/test_fields/fields_aiida.node.process.workflow.WorkflowNode.yml index d8928ee1a4..6639d94fba 100644 --- a/tests/orm/test_fields/fields_aiida.node.process.workflow.WorkflowNode.yml +++ b/tests/orm/test_fields/fields_aiida.node.process.workflow.WorkflowNode.yml @@ -1,12 +1,14 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], +attributes: + QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) +computer: QbNumericField('computer', dtype=typing.Optional[str], is_attribute=False) ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) exception: QbStrField('exception', dtype=typing.Optional[str], is_attribute=True) exit_message: QbStrField('exit_message', dtype=typing.Optional[str], is_attribute=True) exit_status: QbNumericField('exit_status', dtype=typing.Optional[int], is_attribute=True) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], +extras: + QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], is_attribute=False, is_subscriptable=True) label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) @@ -17,9 +19,11 @@ process_label: QbStrField('process_label', dtype=typing.Optional[str], is_attrib process_state: QbStrField('process_state', dtype=typing.Optional[str], is_attribute=True) process_status: QbStrField('process_status', dtype=typing.Optional[str], is_attribute=True) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, +repository_content: + QbDictField('repository_content', dtype=typing.Optional[dict[str, bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, +repository_metadata: + QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, typing.Any]], is_attribute=False) sealed: QbField('sealed', dtype=, is_attribute=True) user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.node.process.workflow.workchain.WorkChainNode.yml b/tests/orm/test_fields/fields_aiida.node.process.workflow.workchain.WorkChainNode.yml index d8928ee1a4..6639d94fba 100644 --- a/tests/orm/test_fields/fields_aiida.node.process.workflow.workchain.WorkChainNode.yml +++ b/tests/orm/test_fields/fields_aiida.node.process.workflow.workchain.WorkChainNode.yml @@ -1,12 +1,14 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], +attributes: + QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) +computer: QbNumericField('computer', dtype=typing.Optional[str], is_attribute=False) ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) exception: QbStrField('exception', dtype=typing.Optional[str], is_attribute=True) exit_message: QbStrField('exit_message', dtype=typing.Optional[str], is_attribute=True) exit_status: QbNumericField('exit_status', dtype=typing.Optional[int], is_attribute=True) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], +extras: + QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], is_attribute=False, is_subscriptable=True) label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) @@ -17,9 +19,11 @@ process_label: QbStrField('process_label', dtype=typing.Optional[str], is_attrib process_state: QbStrField('process_state', dtype=typing.Optional[str], is_attribute=True) process_status: QbStrField('process_status', dtype=typing.Optional[str], is_attribute=True) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, +repository_content: + QbDictField('repository_content', dtype=typing.Optional[dict[str, bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, +repository_metadata: + QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, typing.Any]], is_attribute=False) sealed: QbField('sealed', dtype=, is_attribute=True) user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False) diff --git a/tests/orm/test_fields/fields_aiida.node.process.workflow.workfunction.WorkFunctionNode.yml b/tests/orm/test_fields/fields_aiida.node.process.workflow.workfunction.WorkFunctionNode.yml index d8928ee1a4..6639d94fba 100644 --- a/tests/orm/test_fields/fields_aiida.node.process.workflow.workfunction.WorkFunctionNode.yml +++ b/tests/orm/test_fields/fields_aiida.node.process.workflow.workfunction.WorkFunctionNode.yml @@ -1,12 +1,14 @@ -attributes: QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], +attributes: + QbDictField('attributes', dtype=typing.Optional[typing.Dict[str, typing.Any]], is_attribute=False, is_subscriptable=True) -computer: QbNumericField('computer', dtype=typing.Optional[int], is_attribute=False) +computer: QbNumericField('computer', dtype=typing.Optional[str], is_attribute=False) ctime: QbNumericField('ctime', dtype=typing.Optional[datetime.datetime], is_attribute=False) description: QbStrField('description', dtype=typing.Optional[str], is_attribute=False) exception: QbStrField('exception', dtype=typing.Optional[str], is_attribute=True) exit_message: QbStrField('exit_message', dtype=typing.Optional[str], is_attribute=True) exit_status: QbNumericField('exit_status', dtype=typing.Optional[int], is_attribute=True) -extras: QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], +extras: + QbDictField('extras', dtype=typing.Optional[typing.Dict[str, typing.Any]], is_attribute=False, is_subscriptable=True) label: QbStrField('label', dtype=typing.Optional[str], is_attribute=False) mtime: QbNumericField('mtime', dtype=typing.Optional[datetime.datetime], is_attribute=False) @@ -17,9 +19,11 @@ process_label: QbStrField('process_label', dtype=typing.Optional[str], is_attrib process_state: QbStrField('process_state', dtype=typing.Optional[str], is_attribute=True) process_status: QbStrField('process_status', dtype=typing.Optional[str], is_attribute=True) process_type: QbStrField('process_type', dtype=typing.Optional[str], is_attribute=False) -repository_content: QbDictField('repository_content', dtype=typing.Optional[dict[str, +repository_content: + QbDictField('repository_content', dtype=typing.Optional[dict[str, bytes]], is_attribute=False) -repository_metadata: QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, +repository_metadata: + QbDictField('repository_metadata', dtype=typing.Optional[typing.Dict[str, typing.Any]], is_attribute=False) sealed: QbField('sealed', dtype=, is_attribute=True) user: QbNumericField('user', dtype=typing.Optional[int], is_attribute=False)