Merge branch 'feature/909-define-basic-trait-type-using-dataclasses' into feature/911-new-traits-based-integrator

This commit is contained in:
Ondřej Samohel 2024-11-05 16:19:19 +01:00
commit be9ac89057
No known key found for this signature in database
GPG key ID: 02376E18990A97C6
12 changed files with 357 additions and 46 deletions

View file

@ -21,4 +21,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: chartboost/ruff-action@v1
- uses: astral-sh/ruff-action@v1
with:
changed-files: "true"

View file

@ -1,15 +1,26 @@
"""Trait classes for the pipeline."""
from .color import ColorManaged
from .content import (
Bundle,
Compressed,
FileLocation,
Fragment,
LocatableContent,
MimeType,
RootlessLocation,
)
from .cryptography import DigitallySigned, GPGSigned
from .lifecycle import Persistent, Transient
from .meta import Tagged, TemplatePath
from .three_dimensional import Spatial
from .time import Clip, GapPolicy, Sequence, SMPTETimecode
from .three_dimensional import Geometry, IESProfile, Lighting, Shader, Spatial
from .time import (
FrameRanged,
GapPolicy,
Handles,
Sequence,
SMPTETimecode,
Static,
)
from .trait import Representation, TraitBase
from .two_dimensional import (
UDIM,
@ -31,6 +42,15 @@ __all__ = [
"FileLocation",
"MimeType",
"RootlessLocation",
"Fragment",
"LocatableContent",
# color
"ColorManaged",
# cryptography
"DigitallySigned",
"GPGSigned",
# life cycle
"Persistent",
@ -50,10 +70,16 @@ __all__ = [
"UDIM",
# three-dimensional
"Geometry",
"IESProfile",
"Lighting",
"Shader",
"Spatial",
# time
"Clip",
"FrameRanged",
"Static",
"Handles",
"GapPolicy",
"Sequence",
"SMPTETimecode",

View file

@ -0,0 +1,31 @@
"""Color management related traits."""
from __future__ import annotations
from typing import ClassVar, Optional
from pydantic import Field
from .trait import TraitBase
class ColorManaged(TraitBase):
"""Color managed trait.
Holds color management information. Can be used with Image related
traits to define color space and config.
Sync with OpenAssetIO MediaCreation Traits.
Attributes:
color_space (str): An OCIO colorspace name available
in the "current" OCIO context.
config (str): An OCIO config name defining color space.
"""
id: ClassVar[str] = "ayon.color.ColorManaged.v1"
name: ClassVar[str] = "ColorManaged"
description: ClassVar[str] = "Color Managed trait."
color_space: str = Field(
...,
description="Color space."
)
config: Optional[str] = Field(None, description="Color config.")

View file

@ -13,13 +13,17 @@ from .trait import Representation, TraitBase
class MimeType(TraitBase):
"""MimeType trait model.
This model represents a mime type trait.
This model represents a mime type trait. For example, image/jpeg.
It is used to describe the type of content in a representation regardless
of the file extension.
For more information, see RFC 2046 and RFC 4288 (and related RFCs).
Attributes:
name (str): Trait name.
description (str): Trait description.
id (str): id should be namespaced trait name with version
mime_type (str): Mime type.
mime_type (str): Mime type like image/jpeg.
"""
@ -28,10 +32,35 @@ class MimeType(TraitBase):
id: ClassVar[str] = "ayon.content.MimeType.v1"
mime_type: str = Field(..., title="Mime Type")
class FileLocation(TraitBase):
class LocatableContent(TraitBase):
"""LocatableContent trait model.
This model represents a locatable content trait. Locatable content
is content that has a location. It doesn't have to be a file - it could
be a URL or some other location.
Sync with OpenAssetIO MediaCreation Traits.
Attributes:
name (str): Trait name.
description (str): Trait description.
id (str): id should be namespaced trait name with version
location (str): Location.
"""
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(None, title="Is Templated")
class FileLocation(LocatableContent):
"""FileLocation trait model.
This model represents a file location trait.
This model represents a file path. It is a specialization of the
LocatableContent trait. It is adding optional file size and file hash
for easy access to file information.
Attributes:
name (str): Trait name.
@ -46,14 +75,22 @@ class FileLocation(TraitBase):
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: int = Field(..., title="File Size")
file_path: Path = Field(..., title="File Path", alias="location")
file_size: int = Field(None, title="File Size")
file_hash: Optional[str] = Field(None, title="File Hash")
class RootlessLocation(TraitBase):
"""RootlessLocation trait model.
This model represents a rootless location trait.
RootlessLocation trait is a trait that represents a file path that is
without specific root. To obtain absolute path, the root needs to be
resolved by AYON. Rootless path can be used on multiple platforms.
Example::
RootlessLocation(
rootless_path="{root[work]}/project/asset/asset.jpg"
)
Attributes:
name (str): Trait name.
@ -72,7 +109,12 @@ class RootlessLocation(TraitBase):
class Compressed(TraitBase):
"""Compressed trait model.
This model represents a compressed trait.
This trait can hold information about compressed content. What type
of compression is used.
Example::
Compressed("gzip")
Attributes:
name (str): Trait name.
@ -96,6 +138,29 @@ class Bundle(TraitBase):
a collection of representations that are part of a single
entity.
Example::
Bundle(
items=[
[
Representation(
traits=[
MimeType(mime_type="image/jpeg"),
FileLocation(file_path="/path/to/file.jpg")
]
)
],
[
Representation(
traits=[
MimeType(mime_type="image/png"),
FileLocation(file_path="/path/to/file.png")
]
)
]
]
)
Attributes:
name (str): Trait name.
description (str): Trait description.
@ -119,7 +184,19 @@ class Fragment(TraitBase):
"""Fragment trait model.
This model represents a fragment trait. A fragment is a part of
a larger entity that is represented by a representation.
a larger entity that is represented by another representation.
Example::
main_representation = Representation(name="parent",
traits=[],
)
fragment_representation = Representation(
name="fragment",
traits=[
Fragment(parent=main_representation.id),
]
)
Attributes:
name (str): Trait name.

View file

@ -0,0 +1,42 @@
"""Cryptography traits."""
from __future__ import annotations
from typing import ClassVar, Optional
from pydantic import Field
from .trait import TraitBase
class DigitallySigned(TraitBase):
"""Digitally signed trait.
This type trait means that the data is digitally signed.
Attributes:
signature (str): Digital signature.
"""
id: ClassVar[str] = "ayon.cryptography.DigitallySigned.v1"
name: ClassVar[str] = "DigitallySigned"
description: ClassVar[str] = "Digitally signed trait."
class GPGSigned(DigitallySigned):
"""GPG signed trait.
This trait holds GPG signed data.
Attributes:
signature (str): GPG signature.
"""
id: ClassVar[str] = "ayon.cryptography.GPGSigned.v1"
name: ClassVar[str] = "GPGSigned"
description: ClassVar[str] = "GPG signed trait."
signed_data: str = Field(
...,
description="Signed data."
)
clear_text: Optional[str] = Field(
None,
description="Clear text."
)

View file

@ -1,30 +1,44 @@
"""Lifecycle traits."""
from typing import ClassVar
from . import Representation
from .trait import TraitBase
class Transient(TraitBase):
"""Transient trait model.
This model represents a transient trait.
Transient trait marks representation as transient. Such representations
are not persisted in the system.
Attributes:
name (str): Trait name.
description (str): Trait description.
id (str): id should be namespaced trait name with version
tags (List[str]): Tags.
"""
name: ClassVar[str] = "Transient"
description: ClassVar[str] = "Transient Trait Model"
id: ClassVar[str] = "ayon.lifecycle.Transient.v1"
def validate(self, representation: Representation) -> bool:
"""Validate representation is not Persistent.
Args:
representation (Representation): Representation model.
Returns:
bool: True if representation is valid, False otherwise.
"""
return not representation.contains_trait(Persistent)
class Persistent(TraitBase):
"""Persistent trait model.
This model represents a persistent trait.
Persistent trait is opposite to transient trait. It marks representation
as persistent. Such representations are persisted in the system (e.g. in
the database).
Attributes:
name (str): Trait name.
@ -35,3 +49,14 @@ class Persistent(TraitBase):
name: ClassVar[str] = "Persistent"
description: ClassVar[str] = "Persistent Trait Model"
id: ClassVar[str] = "ayon.lifecycle.Persistent.v1"
def validate(self, representation: Representation) -> bool:
"""Validate representation is not Transient.
Args:
representation (Representation): Representation model.
Returns:
bool: True if representation is valid, False otherwise.
"""
return not representation.contains_trait(Transient)

View file

@ -9,7 +9,11 @@ from .trait import TraitBase
class Tagged(TraitBase):
"""Tagged trait model.
This model represents a tagged trait.
This trait can hold list of tags.
Example::
Tagged(tags=["tag1", "tag2"])
Attributes:
name (str): Trait name.
@ -28,12 +32,17 @@ class TemplatePath(TraitBase):
"""TemplatePath trait model.
This model represents a template path with formatting data.
Template path can be Anatomy template and data is used to format it.
Example::
TemplatePath(template="path/{key}/file", data={"key": "to"})
Attributes:
name (str): Trait name.
description (str): Trait description.
id (str): id should be namespaced trait name with version
template_path (str): Template path.
template (str): Template path.
data (dict[str]): Formatting data.
"""

View file

@ -1,4 +1,4 @@
"""Two-dimensional image traits."""
"""3D traits."""
from typing import ClassVar
from pydantic import Field
@ -9,6 +9,17 @@ from .trait import TraitBase
class Spatial(TraitBase):
"""Spatial trait model.
Trait describing spatial information. Up axis valid strings are
"Y", "Z", "X". Handedness valid strings are "left", "right". Meters per
unit is a float value.
Example::
Spatial(up_axis="Y", handedness="right", meters_per_unit=1.0)
Todo:
* Add value validation for up_axis and handedness.
Attributes:
up_axis (str): Up axis.
handedness (str): Handedness.
@ -21,3 +32,50 @@ class Spatial(TraitBase):
up_axis: str = Field(..., title="Up axis")
handedness: str = Field(..., title="Handedness")
meters_per_unit: float = Field(..., title="Meters per unit")
class Geometry(TraitBase):
"""Geometry type trait model.
Type trait for geometry data.
Sync with OpenAssetIO MediaCreation Traits.
"""
id: ClassVar[str] = "ayon.3d.Geometry.v1"
name: ClassVar[str] = "Geometry"
description: ClassVar[str] = "Geometry trait model."
class Shader(TraitBase):
"""Shader trait model.
Type trait for shader data.
Sync with OpenAssetIO MediaCreation Traits.
"""
id: ClassVar[str] = "ayon.3d.Shader.v1"
name: ClassVar[str] = "Shader"
description: ClassVar[str] = "Shader trait model."
class Lighting(TraitBase):
"""Lighting trait model.
Type trait for lighting data.
Sync with OpenAssetIO MediaCreation Traits.
"""
id: ClassVar[str] = "ayon.3d.Lighting.v1"
name: ClassVar[str] = "Lighting"
description: ClassVar[str] = "Lighting trait model."
class IESProfile(TraitBase):
"""IES profile (IES-LM-64) type trait model.
Sync with OpenAssetIO MediaCreation Traits.
"""
id: ClassVar[str] = "ayon.3d.IESProfile.v1"
name: ClassVar[str] = "IESProfile"
description: ClassVar[str] = "IES profile trait model."

View file

@ -12,6 +12,8 @@ from .trait import TraitBase
class GapPolicy(Enum):
"""Gap policy enumeration.
This type defines how to handle gaps in sequence.
Attributes:
forbidden (int): Gaps are forbidden.
missing (int): Gaps are interpreted as missing frames.
@ -23,11 +25,12 @@ class GapPolicy(Enum):
hold = auto()
black = auto()
class FrameRanged(TraitBase):
"""Frame ranged trait model.
class Clip(TraitBase):
"""Clip trait model.
Model representing a frame ranged trait.
Model representing a clip trait.
Sync with OpenAssetIO MediaCreation Traits.
Attributes:
name (str): Trait name.
@ -35,6 +38,37 @@ class Clip(TraitBase):
id (str): id should be namespaced trait name with version
frame_start (int): Frame start.
frame_end (int): Frame end.
frame_in (int): Frame in.
frame_out (int): Frame out.
frames_per_second (int): Frames per second.
step (int): Step.
"""
name: ClassVar[str] = "FrameRanged"
description: ClassVar[str] = "Frame Ranged Trait"
id: ClassVar[str] = "ayon.time.FrameRanged.v1"
frame_start: int = Field(
..., title="Start Frame", alias="start_frame")
frame_end: int = Field(
..., title="Frame Start", alias="end_frame")
frame_in: int = Field(..., title="In Frame", alias="in_frame")
frame_out: int = Field(..., title="Out Frame", alias="out_frame")
frames_per_second: int = Field(
..., title="Frames Per Second", alias="fps")
step: Optional[int] = Field(1, title="Step")
class Handles(TraitBase):
"""Handles trait model.
Handles define the range of frames that are included or excluded
from the sequence.
Attributes:
name (str): Trait name.
description (str): Trait description.
id (str): id should be namespaced trait name with version
inclusive (bool): Handles are inclusive.
frame_start_handle (int): Frame start handle.
frame_end_handle (int): Frame end handle.
@ -42,22 +76,24 @@ class Clip(TraitBase):
name: ClassVar[str] = "Clip"
description: ClassVar[str] = "Clip Trait"
id: ClassVar[str] = "ayon.time.Clip.v1"
frame_start: int = Field(..., title="Frame Start")
frame_end: int = Field(..., title="Frame End")
frame_start_handle: Optional[int] = Field(0, title="Frame Start Handle")
frame_end_handle: Optional[int] = Field(0, title="Frame End Handle")
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")
class Sequence(Clip):
class Sequence(FrameRanged, Handles):
"""Sequence trait model.
This model represents a sequence trait. Based on the Clip trait,
adding handling for steps, gaps policy and frame padding.
This model represents a sequence trait. Based on the FrameRanged trait
and Handles, adding support for gaps policy, frame padding and frame
list specification. Regex is used to match frame numbers.
Attributes:
name (str): Trait name.
description (str): Trait description.
id (str): id should be namespaced trait name with version
step (int): Frame step.
gaps_policy (GapPolicy): Gaps policy - how to handle gaps in
sequence.
frame_padding (int): Frame padding.
@ -70,7 +106,6 @@ class Sequence(Clip):
name: ClassVar[str] = "Sequence"
description: ClassVar[str] = "Sequence Trait Model"
id: ClassVar[str] = "ayon.time.Sequence.v1"
step: Optional[int] = Field(1, title="Step")
gaps_policy: GapPolicy = Field(
GapPolicy.forbidden, title="Gaps Policy")
frame_padding: int = Field(..., title="Frame Padding")
@ -80,8 +115,19 @@ class Sequence(Clip):
# Do we need one for drop and non-drop frame?
class SMPTETimecode(TraitBase):
"""Timecode trait model."""
"""SMPTE Timecode trait model."""
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")
class Static(TraitBase):
"""Static time trait.
Used to define static time (single frame).
"""
name: ClassVar[str] = "Static"
description: ClassVar[str] = "Static Time Trait"
id: ClassVar[str] = "ayon.time.Static.v1"

View file

@ -9,7 +9,7 @@ from .trait import TraitBase
class Image(TraitBase):
"""Image trait model.
This model represents an image trait.
Type trait model for image.
Attributes:
name (str): Trait name.
@ -26,7 +26,7 @@ class Image(TraitBase):
class PixelBased(TraitBase):
"""PixelBased trait model.
This model represents a pixel based trait.
Pixel related trait for image data.
Attributes:
name (str): Trait name.
@ -51,9 +51,10 @@ class Planar(TraitBase):
This model represents an Image with planar configuration.
TODO (antirotor): Is this really a planar configuration? As with
bitplanes and everything? If it serves as differentiator for
Deep images, should it be named differently? Like Raster?
Todo:
* (antirotor): Is this really a planar configuration? As with
bitplanes and everything? If it serves as differentiator for
Deep images, should it be named differently? Like Raster?
Attributes:
name (str): Trait name.
@ -72,29 +73,25 @@ class Planar(TraitBase):
class Deep(TraitBase):
"""Deep trait model.
This model represents a deep image trait.
Type trait model for deep EXR images.
Attributes:
name (str): Trait name.
description (str): Trait description.
id (str): id should be namespaced trait name with version
deep_data_type (str): Deep data type.
"""
name: ClassVar[str] = "Deep"
description: ClassVar[str] = "Deep Trait Model"
id: ClassVar[str] = "ayon.2d.Deep.v1"
deep_data_type: str = Field(..., title="Deep Data Type")
class Overscan(TraitBase):
"""Overscan trait model.
This model represents an overscan (or underscan) trait.
This model represents an overscan (or underscan) trait. Defines the
extra pixels around the image.
Attributes:
name (str): Trait name.

View file

@ -16,7 +16,6 @@ from ayon_api.operations import (
# new_representation_entity,
new_version_entity,
)
from ayon_core.pipeline.publish import (
get_publish_template_name,
)

View file

@ -1 +0,0 @@
"""Test for pyblish plugins."""