Module: dataclass_field_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 inspect
from dataclasses import Field
from dataclasses import dataclass
from typing import Type
from typing_extensions import Self
from cl.runtime.schema.field_decl import FieldDecl


@dataclass(slots=True, kw_only=True)
class DataclassFieldDecl(FieldDecl):
    """Field declaration for a dataclass type."""

    @classmethod
    def create(cls, record_type: Type, field: Field, field_type: Type, field_comment: str) -> Self:
        """
        Create from dataclass field definition and field_type obtained from get_type_hints.

        Notes:
            The Field object also contains the type but it is not resolved in case of ForwardRef.
            For this reason, field_type obtained from get_type_hints is used as well.

        Args:
            record_type: Type of the record for which the field is defined
            field: Dataclass field definition
            field_type: Type of the field obtained from get_type_hints where ForwardRefs are resolved
            field_comment: Field comment (docstring), currently requires source parsing due Python limitations
        """

        # Use base class to populate fields that do not require access to dataclasses metadata
        result = FieldDecl.create(record_type, field.name, field_type, field_comment)

        # Populate fields that require access to dataclasses metadata
        metadata = dict(field.metadata)
        if (name := metadata.pop("name", None)) is not None:
            result.name = name
        if (label := metadata.pop("label", None)) is not None:
            result.label = label
        if (formatter := metadata.pop("formatter", None)) is not None:
            result.formatter = formatter
        if (subtype := metadata.pop("subtype", None)) is not None:
            if subtype == "long":
                if result.field_type == "int":
                    result.field_type = "long"
                else:
                    raise RuntimeError(f"Subtype 'long' is not valid for field type {result.field_type}")
            else:
                raise RuntimeError(f"Subtype {subtype} is not recognized.")

        # Check that no parsed fields remained in metadata
        if len(metadata) > 0:
            raise RuntimeError(f"Unrecognized attributes in dataclass field metadata: {metadata.keys()}")

        return result

Classes

class DataclassFieldDecl (*, name: str = None, label: str | None = None, comment: str | None = None, field_kind: Literal['primitive', 'enum', 'key', 'data'] = None, field_type: str = None, container_type: str | None = None, optional_field: bool = False, optional_values: bool = False, additive: bool = False, formatter: str | None = None, alternate_of: str | None = None)

Field declaration for a dataclass type.

Expand source code
@dataclass(slots=True, kw_only=True)
class DataclassFieldDecl(FieldDecl):
    """Field declaration for a dataclass type."""

    @classmethod
    def create(cls, record_type: Type, field: Field, field_type: Type, field_comment: str) -> Self:
        """
        Create from dataclass field definition and field_type obtained from get_type_hints.

        Notes:
            The Field object also contains the type but it is not resolved in case of ForwardRef.
            For this reason, field_type obtained from get_type_hints is used as well.

        Args:
            record_type: Type of the record for which the field is defined
            field: Dataclass field definition
            field_type: Type of the field obtained from get_type_hints where ForwardRefs are resolved
            field_comment: Field comment (docstring), currently requires source parsing due Python limitations
        """

        # Use base class to populate fields that do not require access to dataclasses metadata
        result = FieldDecl.create(record_type, field.name, field_type, field_comment)

        # Populate fields that require access to dataclasses metadata
        metadata = dict(field.metadata)
        if (name := metadata.pop("name", None)) is not None:
            result.name = name
        if (label := metadata.pop("label", None)) is not None:
            result.label = label
        if (formatter := metadata.pop("formatter", None)) is not None:
            result.formatter = formatter
        if (subtype := metadata.pop("subtype", None)) is not None:
            if subtype == "long":
                if result.field_type == "int":
                    result.field_type = "long"
                else:
                    raise RuntimeError(f"Subtype 'long' is not valid for field type {result.field_type}")
            else:
                raise RuntimeError(f"Subtype {subtype} is not recognized.")

        # Check that no parsed fields remained in metadata
        if len(metadata) > 0:
            raise RuntimeError(f"Unrecognized attributes in dataclass field metadata: {metadata.keys()}")

        return result

Ancestors

Static methods

def create(record_type: Type, field: dataclasses.Field, field_type: Type, field_comment: str) -> Self

Create from dataclass field definition and field_type obtained from get_type_hints.

Notes

The Field object also contains the type but it is not resolved in case of ForwardRef. For this reason, field_type obtained from get_type_hints is used as well.

Args

record_type
Type of the record for which the field is defined
field
Dataclass field definition
field_type
Type of the field obtained from get_type_hints where ForwardRefs are resolved
field_comment
Field comment (docstring), currently requires source parsing due Python limitations

Fields

var additive -> bool

Inherited from: FieldDecl.additive

Optional flag indicating if the element is additive (i.e., its sum across records has meaning).

var alternate_of -> str | None

Inherited from: FieldDecl.alternate_of

This field is an alternate of the specified field, of which only one can be specified.

var comment -> str | None

Inherited from: FieldDecl.comment

Field comment.

var container_type -> str | None

Inherited from: FieldDecl.container_type

Container type name for builtins module and module.ClassName for other types.

var field_kind -> Literal['primitive', 'enum', 'key', 'data']

Inherited from: FieldDecl.field_kind

Kind of the element within the container if the field is a container, otherwise kind of the field itself.

var field_type -> str

Inherited from: FieldDecl.field_type

Field type name for builtins and uuid modules and module.ClassName for all other types.

var formatter -> str | None

Inherited from: FieldDecl.formatter

Format string used to display the element using Python conventions .

var label -> str | None

Inherited from: FieldDecl.label

Field label (if not specified, titleized name is used instead).

var name -> str

Inherited from: FieldDecl.name

Field name.

var optional_field -> bool

Inherited from: FieldDecl.optional_field

Indicates if the entire field can be None.

var optional_values -> bool

Inherited from: FieldDecl.optional_values

Indicates if values within the container can be None if the field is a container, otherwise None.