Module: dataclass_type_decl

Expand source code
# Copyright (C) 2023-present The Project Contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import dataclasses
from dataclasses import dataclass
from typing import Set
from typing import Type
from typing import get_type_hints
from memoization import cached
from typing_extensions import Self
from cl.runtime.schema.element_decl import ElementDecl
from cl.runtime.schema.for_dataclasses.dataclass_field_decl import DataclassFieldDecl
from cl.runtime.schema.type_decl import TypeDecl
from cl.runtime.schema.type_decl import for_type_key_maker


@dataclass(slots=True, kw_only=True)
class DataclassTypeDecl(TypeDecl):
    """Type declaration for a dataclass."""

    @classmethod
    @cached(custom_key_maker=for_type_key_maker)
    def for_type(
        cls,
        record_type: Type,
        *,
        dependencies: Set[Type] | None = None,
        skip_fields: bool = False,
        skip_handlers: bool = False,
    ) -> Self:
        """
        Create or return cached object for the specified record type.

        Args:
            record_type: Type of the record for which the declaration is created
            dependencies: Set of types used in field or methods of the specified type, populated only if not None
            skip_fields: Use this flag to skip fields generation when the method is invoked from a derived class
            skip_handlers: Use this flag to skip handlers generation when the method is invoked internal methods
        """

        if not dataclasses.is_dataclass(record_type):
            raise RuntimeError(f"DataclassTypeDecl used for {record_type.__name__} which is not a dataclass.")

        # Populate using TypeDecl base
        result = TypeDecl.for_type(record_type, dependencies=dependencies, skip_fields=True)

        # Use this flag to skip fields generation when the method is invoked from a derived class
        if not skip_fields:
            # Information about dataclass fields including the metadata (does not resolve ForwardRefs)
            fields = dataclasses.fields(record_type)

            # Get type hints to resolve ForwardRefs
            type_hints = get_type_hints(record_type)

            # Dictionary of member comments (docstrings), currently requires source parsing due Python limitations
            member_comments = cls.get_member_comments(record_type)

            # Add elements
            result.elements = []
            for field in fields:
                # Get type from type hints because they resolve forward references
                field_type = type_hints[field.name]

                # Field comment (docstring)
                field_comment = member_comments.get(field.name, None)

                # Get the rest of the data from the field itself
                field_decl = DataclassFieldDecl.create(record_type, field, field_type, field_comment)

                # Convert to element and add
                element_decl = ElementDecl.create(field_decl)
                result.elements.append(element_decl)

        return result

Classes

class DataclassTypeDecl (*, module: ModuleDeclKey = None, name: str = None, label: str | None = None, comment: str | None = None, kind: TypeKind | None = None, display_kind: DisplayKindLiteral = None, inherit: TypeDeclKey | None = None, declare: HandlerDeclareBlockDecl | None = None, elements: List[ElementDecl] | None = None, keys: List[str] | None = None, immutable: bool | None = None, permanent: bool | None = None)

Type declaration for a dataclass.

Expand source code
@dataclass(slots=True, kw_only=True)
class DataclassTypeDecl(TypeDecl):
    """Type declaration for a dataclass."""

    @classmethod
    @cached(custom_key_maker=for_type_key_maker)
    def for_type(
        cls,
        record_type: Type,
        *,
        dependencies: Set[Type] | None = None,
        skip_fields: bool = False,
        skip_handlers: bool = False,
    ) -> Self:
        """
        Create or return cached object for the specified record type.

        Args:
            record_type: Type of the record for which the declaration is created
            dependencies: Set of types used in field or methods of the specified type, populated only if not None
            skip_fields: Use this flag to skip fields generation when the method is invoked from a derived class
            skip_handlers: Use this flag to skip handlers generation when the method is invoked internal methods
        """

        if not dataclasses.is_dataclass(record_type):
            raise RuntimeError(f"DataclassTypeDecl used for {record_type.__name__} which is not a dataclass.")

        # Populate using TypeDecl base
        result = TypeDecl.for_type(record_type, dependencies=dependencies, skip_fields=True)

        # Use this flag to skip fields generation when the method is invoked from a derived class
        if not skip_fields:
            # Information about dataclass fields including the metadata (does not resolve ForwardRefs)
            fields = dataclasses.fields(record_type)

            # Get type hints to resolve ForwardRefs
            type_hints = get_type_hints(record_type)

            # Dictionary of member comments (docstrings), currently requires source parsing due Python limitations
            member_comments = cls.get_member_comments(record_type)

            # Add elements
            result.elements = []
            for field in fields:
                # Get type from type hints because they resolve forward references
                field_type = type_hints[field.name]

                # Field comment (docstring)
                field_comment = member_comments.get(field.name, None)

                # Get the rest of the data from the field itself
                field_decl = DataclassFieldDecl.create(record_type, field, field_type, field_comment)

                # Convert to element and add
                element_decl = ElementDecl.create(field_decl)
                result.elements.append(element_decl)

        return result

Ancestors

Static methods

def by_pair(iterable)

Inherited from: TypeDecl.by_pair

s -> (s0,s1), (s1,s2), (s2, s3), …

def for_class_path(class_path: str) -> Self

Inherited from: TypeDecl.for_class_path

Create or return cached object for the specified class path in module.ClassName format.

def for_key(key: TypeDeclKey) -> Self

Inherited from: TypeDecl.for_key

Create or return cached object for the specified type declaration key.

def for_type(cls, record_type: Type, *, dependencies: Optional[Set[Type]] = None, skip_fields: bool = False, skip_handlers: bool = False) -> Self

Inherited from: TypeDecl.for_type

Create or return cached object for the specified record type …

def get_key_type() -> Type

Inherited from: TypeDecl.get_key_type

Return key type even when called from a record.

def get_member_comments(cls, record_type: type) -> Dict[str, str]

Inherited from: TypeDecl.get_member_comments

Extract class member comments.

Fields

var comment -> str | None

Inherited from: TypeDecl.comment

Type comment. Contains additional information.

var declare -> HandlerDeclareBlockDecl | None

Inherited from: TypeDecl.declare

Handler declaration block.

var display_kind -> Literal['Basic', 'Singleton', 'Dashboard']

Inherited from: TypeDecl.display_kind

Display kind.

var elements -> Optional[List[ElementDecl]]

Inherited from: TypeDecl.elements

Element declaration block.

var immutable -> bool | None

Inherited from: TypeDecl.immutable

Immutable flag.

var inherit -> TypeDeclKey | None

Inherited from: TypeDecl.inherit

Parent type reference.

var keys -> Optional[List[str]]

Inherited from: TypeDecl.keys

Array of key element names (specify in base class only).

var kind -> Optional[Literal['final', 'abstract', 'element', 'abstract_element']]

Inherited from: TypeDecl.kind

Type kind.

var label -> str | None

Inherited from: TypeDecl.label

Type label.

var module -> ModuleDeclKey

Inherited from: TypeDecl.module

Module reference.

var name -> str

Inherited from: TypeDecl.name

Type name is unique when combined with module.

var permanent -> bool | None

Inherited from: TypeDecl.permanent

When the record is saved, also save it permanently.

Methods

def get_key(self) -> TypeDeclKey

Inherited from: TypeDecl.get_key

Return a new key object whose fields populated from self, do not return self.

def init_all(self) -> None

Inherited from: TypeDecl.init_all

Invoke ‘init’ for each class in the order from base to derived, then validate against schema.

def to_type_decl_dict(self) -> Dict[str, Any]

Inherited from: TypeDecl.to_type_decl_dict

Convert to dictionary using type declaration conventions.