From 534be2c64e0f2ac0f3355f4d413c3edc0fcde3f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Mon, 3 Mar 2025 16:31:26 +0100 Subject: [PATCH] :recycle: change pydantic models to pure dataclasses --- client/ayon_core/pipeline/traits/README.md | 9 ++- client/ayon_core/pipeline/traits/color.py | 12 ++-- client/ayon_core/pipeline/traits/content.py | 47 ++++++++++------ .../ayon_core/pipeline/traits/cryptography.py | 21 +++---- client/ayon_core/pipeline/traits/lifecycle.py | 16 +++++- client/ayon_core/pipeline/traits/meta.py | 54 +++++++++++------- client/ayon_core/pipeline/traits/temporal.py | 55 ++++++++++--------- .../pipeline/traits/three_dimensional.py | 19 +++++-- client/ayon_core/pipeline/traits/trait.py | 23 +------- .../pipeline/traits/two_dimensional.py | 42 +++++++++----- client/pyproject.toml | 1 - pyproject.toml | 1 - 12 files changed, 174 insertions(+), 126 deletions(-) diff --git a/client/ayon_core/pipeline/traits/README.md b/client/ayon_core/pipeline/traits/README.md index aa38d30a8e..1566b4f353 100644 --- a/client/ayon_core/pipeline/traits/README.md +++ b/client/ayon_core/pipeline/traits/README.md @@ -184,10 +184,13 @@ to different packages based on their use: | | Overscan | holds overscan/underscan information (added pixels to bottom/sides) | | UDIM | Representation is UDIM tile set -Traits are [Pydantic models](https://docs.pydantic.dev/latest/) with optional +Traits are Python data classes with optional validation and helper methods. If they implement `TraitBase.validate(Representation)` method, they can validate against all other traits -in the representation if needed. They can also implement pydantic form of -data validators. +in the representation if needed. + +> [!NOTE] +> They could be easily converted to [Pydantic models](https://docs.pydantic.dev/latest/) but since this must run in diverse Python environments inside DCC, we cannot +> easily resolve pydantic-core dependency (as it is binary written in Rust). > [!NOTE] > Every trait has id, name and some human readable description. Every trait diff --git a/client/ayon_core/pipeline/traits/color.py b/client/ayon_core/pipeline/traits/color.py index b816593624..491131c8bc 100644 --- a/client/ayon_core/pipeline/traits/color.py +++ b/client/ayon_core/pipeline/traits/color.py @@ -1,13 +1,13 @@ """Color management related traits.""" from __future__ import annotations +from dataclasses import dataclass from typing import ClassVar, Optional -from pydantic import Field - from .trait import TraitBase +@dataclass class ColorManaged(TraitBase): """Color managed trait. @@ -24,9 +24,7 @@ class ColorManaged(TraitBase): id: ClassVar[str] = "ayon.color.ColorManaged.v1" name: ClassVar[str] = "ColorManaged" + color_space: str description: ClassVar[str] = "Color Managed trait." - color_space: str = Field( - ..., - description="Color space." - ) - config: Optional[str] = Field(default=None, description="Color config.") + persistent: ClassVar[bool] = True + config: Optional[str] = None diff --git a/client/ayon_core/pipeline/traits/content.py b/client/ayon_core/pipeline/traits/content.py index 4ee63b2b08..9bb43fcdb3 100644 --- a/client/ayon_core/pipeline/traits/content.py +++ b/client/ayon_core/pipeline/traits/content.py @@ -3,13 +3,12 @@ from __future__ import annotations import contextlib import re +from dataclasses import dataclass # TC003 is there because Path in TYPECHECKING will fail in tests from pathlib import Path # noqa: TC003 from typing import ClassVar, Generator, Optional -from pydantic import Field - from .representation import Representation from .temporal import FrameRanged, Handles, Sequence from .trait import ( @@ -21,6 +20,7 @@ from .two_dimensional import UDIM from .utils import get_sequence_from_files +@dataclass class MimeType(TraitBase): """MimeType trait model. @@ -40,9 +40,11 @@ class MimeType(TraitBase): name: ClassVar[str] = "MimeType" description: ClassVar[str] = "MimeType Trait Model" id: ClassVar[str] = "ayon.content.MimeType.v1" - mime_type: str = Field(..., title="Mime Type") + persistent: ClassVar[bool] = True + mime_type: str +@dataclass class LocatableContent(TraitBase): """LocatableContent trait model. @@ -57,15 +59,18 @@ class LocatableContent(TraitBase): description (str): Trait description. id (str): id should be namespaced trait name with version location (str): Location. + is_templated (Optional[bool]): Is the location templated? Default is None. """ name: ClassVar[str] = "LocatableContent" description: ClassVar[str] = "LocatableContent Trait Model" id: ClassVar[str] = "ayon.content.LocatableContent.v1" - location: str = Field(..., title="Location") - is_templated: Optional[bool] = Field(default=None, title="Is Templated") + persistent: ClassVar[bool] = True + location: str + is_templated: Optional[bool] = None +@dataclass class FileLocation(TraitBase): """FileLocation trait model. @@ -78,18 +83,20 @@ class FileLocation(TraitBase): description (str): Trait description. id (str): id should be namespaced trait name with version file_path (str): File path. - file_size (int): File size in bytes. - file_hash (str): File hash. + file_size (Optional[int]): File size in bytes. + file_hash (Optional[str]): File hash. """ name: ClassVar[str] = "FileLocation" description: ClassVar[str] = "FileLocation Trait Model" id: ClassVar[str] = "ayon.content.FileLocation.v1" - file_path: Path = Field(..., title="File Path") - file_size: Optional[int] = Field(default=None, title="File Size") - file_hash: Optional[str] = Field(default=None, title="File Hash") + persistent: ClassVar[bool] = True + file_path: Path + file_size: Optional[int] = None + file_hash: Optional[str] = None +@dataclass class FileLocations(TraitBase): """FileLocation trait model. @@ -108,7 +115,8 @@ class FileLocations(TraitBase): name: ClassVar[str] = "FileLocations" description: ClassVar[str] = "FileLocations Trait Model" id: ClassVar[str] = "ayon.content.FileLocations.v1" - file_paths: list[FileLocation] = Field(..., title="File Path") + persistent: ClassVar[bool] = True + file_paths: list[FileLocation] def get_files(self) -> Generator[Path, None, None]: """Get all file paths from the trait. @@ -340,6 +348,7 @@ class FileLocations(TraitBase): return frame_start_with_handles, frame_end_with_handles +@dataclass class RootlessLocation(TraitBase): """RootlessLocation trait model. @@ -363,9 +372,11 @@ class RootlessLocation(TraitBase): name: ClassVar[str] = "RootlessLocation" description: ClassVar[str] = "RootlessLocation Trait Model" id: ClassVar[str] = "ayon.content.RootlessLocation.v1" - rootless_path: str = Field(..., title="File Path") + persistent: ClassVar[bool] = True + rootless_path: str +@dataclass class Compressed(TraitBase): """Compressed trait model. @@ -386,9 +397,11 @@ class Compressed(TraitBase): name: ClassVar[str] = "Compressed" description: ClassVar[str] = "Compressed Trait" id: ClassVar[str] = "ayon.content.Compressed.v1" - compression_type: str = Field(..., title="Compression Type") + persistent: ClassVar[bool] = True + compression_type: str +@dataclass class Bundle(TraitBase): """Bundle trait model. @@ -424,8 +437,8 @@ class Bundle(TraitBase): name: ClassVar[str] = "Bundle" description: ClassVar[str] = "Bundle Trait" id: ClassVar[str] = "ayon.content.Bundle.v1" - items: list[list[TraitBase]] = Field( - ..., title="Bundles of traits") + persistent: ClassVar[bool] = True + items: list[list[TraitBase]] def to_representations(self) -> Generator[Representation]: """Convert bundle to representations. @@ -438,6 +451,7 @@ class Bundle(TraitBase): yield Representation(name=f"{self.name} {idx}", traits=item) +@dataclass class Fragment(TraitBase): """Fragment trait model. @@ -466,4 +480,5 @@ class Fragment(TraitBase): name: ClassVar[str] = "Fragment" description: ClassVar[str] = "Fragment Trait" id: ClassVar[str] = "ayon.content.Fragment.v1" - parent: str = Field(..., title="Parent Representation Id") + persistent: ClassVar[bool] = True + parent: str diff --git a/client/ayon_core/pipeline/traits/cryptography.py b/client/ayon_core/pipeline/traits/cryptography.py index 3719f3dbbf..d9445bd543 100644 --- a/client/ayon_core/pipeline/traits/cryptography.py +++ b/client/ayon_core/pipeline/traits/cryptography.py @@ -1,13 +1,13 @@ """Cryptography traits.""" from __future__ import annotations +from dataclasses import dataclass from typing import ClassVar, Optional -from pydantic import Field - from .trait import TraitBase +@dataclass class DigitallySigned(TraitBase): """Digitally signed trait. @@ -20,25 +20,22 @@ class DigitallySigned(TraitBase): id: ClassVar[str] = "ayon.cryptography.DigitallySigned.v1" name: ClassVar[str] = "DigitallySigned" description: ClassVar[str] = "Digitally signed trait." + persistent: ClassVar[bool] = True - +@dataclass class PGPSigned(DigitallySigned): """PGP signed trait. This trait holds PGP (RFC-4880) signed data. Attributes: - signature (str): PGP signature. + signed_data (str): Signed data. + clear_text (str): Clear text. """ id: ClassVar[str] = "ayon.cryptography.PGPSigned.v1" name: ClassVar[str] = "PGPSigned" description: ClassVar[str] = "PGP signed trait." - signed_data: str = Field( - ..., - description="Signed data." - ) - clear_text: Optional[str] = Field( - None, - description="Clear text." - ) + persistent: ClassVar[bool] = True + signed_data: str + clear_text: Optional[str] = None diff --git a/client/ayon_core/pipeline/traits/lifecycle.py b/client/ayon_core/pipeline/traits/lifecycle.py index be87a86cbc..6f38525c47 100644 --- a/client/ayon_core/pipeline/traits/lifecycle.py +++ b/client/ayon_core/pipeline/traits/lifecycle.py @@ -1,9 +1,11 @@ """Lifecycle traits.""" +from dataclasses import dataclass from typing import ClassVar from .trait import TraitBase, TraitValidationError +@dataclass class Transient(TraitBase): """Transient trait model. @@ -19,6 +21,7 @@ class Transient(TraitBase): name: ClassVar[str] = "Transient" description: ClassVar[str] = "Transient Trait Model" id: ClassVar[str] = "ayon.lifecycle.Transient.v1" + persistent: ClassVar[bool] = True # see note in Persistent def validate_trait(self, representation) -> None: # noqa: ANN001 """Validate representation is not Persistent. @@ -26,14 +29,16 @@ class Transient(TraitBase): Args: representation (Representation): Representation model. - Returns: - bool: True if representation is valid, False otherwise. + Raises: + TraitValidationError: If representation is marked as both + """ if representation.contains_trait(Persistent): msg = "Representation is marked as both Persistent and Transient." raise TraitValidationError(self.name, msg) +@dataclass class Persistent(TraitBase): """Persistent trait model. @@ -50,6 +55,10 @@ class Persistent(TraitBase): name: ClassVar[str] = "Persistent" description: ClassVar[str] = "Persistent Trait Model" id: ClassVar[str] = "ayon.lifecycle.Persistent.v1" + # note that this affects persistence of the trait itself, not + # the representation. This is a class variable, so it is shared + # among all instances of the class. + persistent: bool = True def validate_trait(self, representation) -> None: # noqa: ANN001 """Validate representation is not Transient. @@ -57,6 +66,9 @@ class Persistent(TraitBase): Args: representation (Representation): Representation model. + Raises: + TraitValidationError: If representation is marked as both + """ if representation.contains_trait(Transient): msg = "Representation is marked as both Persistent and Transient." diff --git a/client/ayon_core/pipeline/traits/meta.py b/client/ayon_core/pipeline/traits/meta.py index 0f8c175af5..3bf4a87a0b 100644 --- a/client/ayon_core/pipeline/traits/meta.py +++ b/client/ayon_core/pipeline/traits/meta.py @@ -1,13 +1,13 @@ """Metadata traits.""" from __future__ import annotations +from dataclasses import dataclass from typing import ClassVar, List, Optional -from pydantic import Field - from .trait import TraitBase +@dataclass class Tagged(TraitBase): """Tagged trait model. @@ -27,9 +27,11 @@ class Tagged(TraitBase): name: ClassVar[str] = "Tagged" description: ClassVar[str] = "Tagged Trait Model" id: ClassVar[str] = "ayon.meta.Tagged.v1" - tags: List[str] = Field(..., title="Tags") + persistent: ClassVar[bool] = True + tags: List[str] +@dataclass class TemplatePath(TraitBase): """TemplatePath trait model. @@ -51,10 +53,12 @@ class TemplatePath(TraitBase): name: ClassVar[str] = "TemplatePath" description: ClassVar[str] = "Template Path Trait Model" id: ClassVar[str] = "ayon.meta.TemplatePath.v1" - template: str = Field(..., title="Template Path") - data: dict = Field(..., title="Formatting Data") + persistent: ClassVar[bool] = True + template: str + data: dict +@dataclass class Variant(TraitBase): """Variant trait model. @@ -75,9 +79,11 @@ class Variant(TraitBase): name: ClassVar[str] = "Variant" description: ClassVar[str] = "Variant Trait Model" id: ClassVar[str] = "ayon.meta.Variant.v1" - variant: str = Field(..., title="Variant") + persistent: ClassVar[bool] = True + variant: str +@dataclass class KeepOriginalLocation(TraitBase): """Keep files in its original location. @@ -88,9 +94,10 @@ class KeepOriginalLocation(TraitBase): name: ClassVar[str] = "KeepOriginalLocation" description: ClassVar[str] = "Keep Original Location Trait Model" id: ClassVar[str] = "ayon.meta.KeepOriginalLocation.v1" - persistent: bool = Field(default=False, title="Persistent") + persistent: ClassVar[bool] = False +@dataclass class KeepOriginalName(TraitBase): """Keep files in its original name. @@ -101,9 +108,10 @@ class KeepOriginalName(TraitBase): name: ClassVar[str] = "KeepOriginalName" description: ClassVar[str] = "Keep Original Name Trait Model" id: ClassVar[str] = "ayon.meta.KeepOriginalName.v1" - persistent: bool = Field(default=False, title="Persistent") + persistent: ClassVar[bool] = False +@dataclass class SourceApplication(TraitBase): """Metadata about the source (producing) application. @@ -115,22 +123,26 @@ class SourceApplication(TraitBase): Note that this is not really connected to any logic in ayon-applications addon. + Attributes: + application (str): Application name. + variant (str): Application variant. + version (str): Application version. + platform (str): Platform name (Windows, darwin, etc.). + host_name (str): AYON host name if applicable. """ name: ClassVar[str] = "SourceApplication" description: ClassVar[str] = "Source Application Trait Model" id: ClassVar[str] = "ayon.meta.SourceApplication.v1" - application: str = Field(..., title="Application Name") - variant: Optional[str] = Field( - None, title="Application Variant (e.g. Pro)") - version: Optional[str] = Field( - None, title="Application Version") - platform: Optional[str] = Field( - None, title="Platform Name (e.g. Windows)") - host_name: Optional[str] = Field( - None, title="AYON host Name if applicable") + persistent: ClassVar[bool] = True + application: str + variant: Optional[str] = None + version: Optional[str] = None + platform: Optional[str] = None + host_name: Optional[str] = None +@dataclass class IntendedUse(TraitBase): """Intended use of the representation. @@ -138,9 +150,13 @@ class IntendedUse(TraitBase): can be used in cases, where the other traits are not enough to describe the intended use. For example txt file with tracking points can be used as corner pin in After Effect but not in Nuke. - """ + Attributes: + use (str): Intended use description. + + """ name: ClassVar[str] = "IntendedUse" description: ClassVar[str] = "Intended Use Trait Model" id: ClassVar[str] = "ayon.meta.IntendedUse.v1" - use: str = Field(..., title="Intended Use") + persistent: ClassVar[bool] = True + use: str diff --git a/client/ayon_core/pipeline/traits/temporal.py b/client/ayon_core/pipeline/traits/temporal.py index 286336ea55..75025da02b 100644 --- a/client/ayon_core/pipeline/traits/temporal.py +++ b/client/ayon_core/pipeline/traits/temporal.py @@ -3,12 +3,12 @@ from __future__ import annotations import contextlib import re +from dataclasses import dataclass from enum import Enum, auto from re import Pattern from typing import TYPE_CHECKING, ClassVar, Optional import clique -from pydantic import Field, field_validator from .trait import MissingTraitError, TraitBase, TraitValidationError @@ -36,6 +36,7 @@ class GapPolicy(Enum): black = auto() +@dataclass class FrameRanged(TraitBase): """Frame ranged trait model. @@ -70,16 +71,16 @@ class FrameRanged(TraitBase): name: ClassVar[str] = "FrameRanged" description: ClassVar[str] = "Frame Ranged Trait" id: ClassVar[str] = "ayon.time.FrameRanged.v1" - frame_start: int = Field( - ..., title="Start Frame") - frame_end: int = Field( - ..., title="Frame Start") - frame_in: Optional[int] = Field(default=None, title="In Frame") - frame_out: Optional[int] = Field(default=None, title="Out Frame") - frames_per_second: str = Field(..., title="Frames Per Second") - step: Optional[int] = Field(default=1, title="Step") + persistent: ClassVar[bool] = True + frame_start: int + frame_end: int + frame_in: Optional[int] = None + frame_out: Optional[int] = None + frames_per_second: str = None + step: Optional[int] = None +@dataclass class Handles(TraitBase): """Handles trait model. @@ -98,14 +99,13 @@ class Handles(TraitBase): name: ClassVar[str] = "Handles" description: ClassVar[str] = "Handles Trait" id: ClassVar[str] = "ayon.time.Handles.v1" - inclusive: Optional[bool] = Field( - False, title="Handles are inclusive") # noqa: FBT003 - frame_start_handle: Optional[int] = Field( - 0, title="Frame Start Handle") - frame_end_handle: Optional[int] = Field( - 0, title="Frame End Handle") + persistent: ClassVar[bool] = True + inclusive: Optional[bool] = False + frame_start_handle: Optional[int] = None + frame_end_handle: Optional[int] = None +@dataclass class Sequence(TraitBase): """Sequence trait model. @@ -130,15 +130,12 @@ class Sequence(TraitBase): name: ClassVar[str] = "Sequence" description: ClassVar[str] = "Sequence Trait Model" id: ClassVar[str] = "ayon.time.Sequence.v1" - gaps_policy: Optional[GapPolicy] = Field( - default=GapPolicy.forbidden, title="Gaps Policy") - frame_padding: int = Field(..., title="Frame Padding") - frame_regex: Optional[Pattern] = Field( - default=None, title="Frame Regex") - frame_spec: Optional[str] = Field(default=None, - title="Frame Specification") + persistent: ClassVar[bool] = True + frame_padding: int + gaps_policy: Optional[GapPolicy] = GapPolicy.forbidden + frame_regex: Optional[Pattern] = None + frame_spec: Optional[str] = None - @field_validator("frame_regex") @classmethod def validate_frame_regex( cls, v: Optional[Pattern] @@ -426,15 +423,22 @@ class Sequence(TraitBase): # Do we need one for drop and non-drop frame? +@dataclass class SMPTETimecode(TraitBase): - """SMPTE Timecode trait model.""" + """SMPTE Timecode trait model. + + Attributes: + timecode (str): SMPTE Timecode HH:MM:SS:FF + """ name: ClassVar[str] = "Timecode" description: ClassVar[str] = "SMPTE Timecode Trait" id: ClassVar[str] = "ayon.time.SMPTETimecode.v1" - timecode: str = Field(..., title="SMPTE Timecode HH:MM:SS:FF") + persistent: ClassVar[bool] = True + timecode: str +@dataclass class Static(TraitBase): """Static time trait. @@ -444,3 +448,4 @@ class Static(TraitBase): name: ClassVar[str] = "Static" description: ClassVar[str] = "Static Time Trait" id: ClassVar[str] = "ayon.time.Static.v1" + persistent: ClassVar[bool] = True diff --git a/client/ayon_core/pipeline/traits/three_dimensional.py b/client/ayon_core/pipeline/traits/three_dimensional.py index 67f4415f73..d68fb99e61 100644 --- a/client/ayon_core/pipeline/traits/three_dimensional.py +++ b/client/ayon_core/pipeline/traits/three_dimensional.py @@ -1,11 +1,11 @@ """3D traits.""" +from dataclasses import dataclass from typing import ClassVar -from pydantic import Field - from .trait import TraitBase +@dataclass class Spatial(TraitBase): """Spatial trait model. @@ -29,11 +29,13 @@ class Spatial(TraitBase): id: ClassVar[str] = "ayon.3d.Spatial.v1" name: ClassVar[str] = "Spatial" description: ClassVar[str] = "Spatial trait model." - up_axis: str = Field(..., title="Up axis") - handedness: str = Field(..., title="Handedness") - meters_per_unit: float = Field(..., title="Meters per unit") + persistent: ClassVar[bool] = True + up_axis: str + handedness: str + meters_per_unit: float +@dataclass class Geometry(TraitBase): """Geometry type trait model. @@ -45,8 +47,10 @@ class Geometry(TraitBase): id: ClassVar[str] = "ayon.3d.Geometry.v1" name: ClassVar[str] = "Geometry" description: ClassVar[str] = "Geometry trait model." + persistent: ClassVar[bool] = True +@dataclass class Shader(TraitBase): """Shader trait model. @@ -58,8 +62,10 @@ class Shader(TraitBase): id: ClassVar[str] = "ayon.3d.Shader.v1" name: ClassVar[str] = "Shader" description: ClassVar[str] = "Shader trait model." + persistent: ClassVar[bool] = True +@dataclass class Lighting(TraitBase): """Lighting trait model. @@ -71,8 +77,10 @@ class Lighting(TraitBase): id: ClassVar[str] = "ayon.3d.Lighting.v1" name: ClassVar[str] = "Lighting" description: ClassVar[str] = "Lighting trait model." + persistent: ClassVar[bool] = True +@dataclass class IESProfile(TraitBase): """IES profile (IES-LM-64) type trait model. @@ -82,3 +90,4 @@ class IESProfile(TraitBase): id: ClassVar[str] = "ayon.3d.IESProfile.v1" name: ClassVar[str] = "IESProfile" description: ClassVar[str] = "IES profile trait model." + persistent: ClassVar[bool] = True diff --git a/client/ayon_core/pipeline/traits/trait.py b/client/ayon_core/pipeline/traits/trait.py index f15c525495..01a7641c59 100644 --- a/client/ayon_core/pipeline/traits/trait.py +++ b/client/ayon_core/pipeline/traits/trait.py @@ -3,16 +3,9 @@ from __future__ import annotations import re from abc import ABC, abstractmethod +from dataclasses import dataclass from typing import TYPE_CHECKING, Generic, Optional, TypeVar -import pydantic.alias_generators -from pydantic import ( - AliasGenerator, - BaseModel, - ConfigDict, - Field, -) - if TYPE_CHECKING: from .representation import Representation @@ -20,25 +13,15 @@ if TYPE_CHECKING: T = TypeVar("T", bound="TraitBase") -class TraitBase(ABC, BaseModel): +@dataclass +class TraitBase(ABC): """Base trait model. This model must be used as a base for all trait models. - It is using Pydantic BaseModel for serialization and validation. ``id``, ``name``, and ``description`` are abstract attributes that must be implemented in the derived classes. """ - model_config = ConfigDict( - alias_generator=AliasGenerator( - serialization_alias=pydantic.alias_generators.to_camel, - ) - ) - - persistent: bool = Field( - default=True, title="Persistent", - description="Whether the trait is persistent (integrated) or not.") - @property @abstractmethod def id(self) -> str: diff --git a/client/ayon_core/pipeline/traits/two_dimensional.py b/client/ayon_core/pipeline/traits/two_dimensional.py index 77aa5767d6..93d7d8c86a 100644 --- a/client/ayon_core/pipeline/traits/two_dimensional.py +++ b/client/ayon_core/pipeline/traits/two_dimensional.py @@ -2,16 +2,16 @@ from __future__ import annotations import re +from dataclasses import dataclass from typing import TYPE_CHECKING, ClassVar, Optional -from pydantic import Field, field_validator - from .trait import TraitBase if TYPE_CHECKING: from .content import FileLocation, FileLocations +@dataclass class Image(TraitBase): """Image trait model. @@ -26,8 +26,10 @@ class Image(TraitBase): name: ClassVar[str] = "Image" description: ClassVar[str] = "Image Trait" id: ClassVar[str] = "ayon.2d.Image.v1" + persistent: ClassVar[bool] = True +@dataclass class PixelBased(TraitBase): """PixelBased trait model. @@ -45,11 +47,13 @@ class PixelBased(TraitBase): name: ClassVar[str] = "PixelBased" description: ClassVar[str] = "PixelBased Trait Model" id: ClassVar[str] = "ayon.2d.PixelBased.v1" - display_window_width: int = Field(..., title="Display Window Width") - display_window_height: int = Field(..., title="Display Window Height") - pixel_aspect_ratio: float = Field(..., title="Pixel Aspect Ratio") + persistent: ClassVar[bool] = True + display_window_width: int + display_window_height: int + pixel_aspect_ratio: float +@dataclass class Planar(TraitBase): """Planar trait model. @@ -57,7 +61,7 @@ class Planar(TraitBase): Todo: * (antirotor): Is this really a planar configuration? As with - bitplanes and everything? If it serves as differentiator for + bit planes and everything? If it serves as differentiator for Deep images, should it be named differently? Like Raster? Attributes: @@ -70,9 +74,11 @@ class Planar(TraitBase): name: ClassVar[str] = "Planar" description: ClassVar[str] = "Planar Trait Model" id: ClassVar[str] = "ayon.2d.Planar.v1" - planar_configuration: str = Field(..., title="Planar-based Image") + persistent: ClassVar[bool] = True + planar_configuration: str +@dataclass class Deep(TraitBase): """Deep trait model. @@ -87,8 +93,10 @@ class Deep(TraitBase): name: ClassVar[str] = "Deep" description: ClassVar[str] = "Deep Trait Model" id: ClassVar[str] = "ayon.2d.Deep.v1" + persistent: ClassVar[bool] = True +@dataclass class Overscan(TraitBase): """Overscan trait model. @@ -108,12 +116,14 @@ class Overscan(TraitBase): name: ClassVar[str] = "Overscan" description: ClassVar[str] = "Overscan Trait" id: ClassVar[str] = "ayon.2d.Overscan.v1" - left: int = Field(..., title="Left Overscan") - right: int = Field(..., title="Right Overscan") - top: int = Field(..., title="Top Overscan") - bottom: int = Field(..., title="Bottom Overscan") + persistent: ClassVar[bool] = True + left: int + right: int + top: int + bottom: int +@dataclass class UDIM(TraitBase): """UDIM trait model. @@ -124,16 +134,18 @@ class UDIM(TraitBase): description (str): Trait description. id (str): id should be namespaced trait name with version udim (int): UDIM value. + udim_regex (str): UDIM regex. """ name: ClassVar[str] = "UDIM" description: ClassVar[str] = "UDIM Trait" id: ClassVar[str] = "ayon.2d.UDIM.v1" - udim: list[int] = Field(..., title="UDIM") - udim_regex: Optional[str] = Field( - default=r"(?:\.|_)(?P\d+)\.\D+\d?$", title="UDIM Regex") + persistent: ClassVar[bool] = True + udim: list[int] + udim_regex: Optional[str] = r"(?:\.|_)(?P\d+)\.\D+\d?$" - @field_validator("udim_regex") + # field validator for udim_regex - this works in pydantic model v2 but not + # with the pure data classes @classmethod def validate_frame_regex(cls, v: Optional[str]) -> Optional[str]: """Validate udim regex. diff --git a/client/pyproject.toml b/client/pyproject.toml index d61baee2c4..edf7f57317 100644 --- a/client/pyproject.toml +++ b/client/pyproject.toml @@ -10,7 +10,6 @@ pyblish-base = "^1.8.11" speedcopy = "^2.1" six = "^1.15" qtawesome = "0.7.3" -pydantic = "^2.9.2" [ayon.runtimeDependencies] aiohttp-middlewares = "^2.0.0" diff --git a/pyproject.toml b/pyproject.toml index ac291ab636..c8256adfca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,6 @@ readme = "README.md" [tool.poetry.dependencies] python = ">=3.9.1,<3.10" -pydantic = "^2.9.2" pre-commit = "^4.0.0" clique = "^2" pyblish-base = "^1.8"