Conversation
|
Hey, would you like to me do some test runs for this PR? |
no tests yet (apart unit tests that were passing recently). It is more work in progress now - main development is now done on the integrator. This is here to discuss traits and if we have everything we need. I guess more will be clear one we try to use them in existing workflows. |
This is most likely how I may test run it by trying to use it in draft PR. |
|
Regarding trait versions: current idea is to store the version in trait id itself - Example: file_location = FileLocation(file_path=Path(...))
# it is class method
file_location.get_version() == FileLocation.get_version()
print(FileLocation.id)
# ayon.content.FileLocation.v1There are some functions on I considered having version encoded in |
|
Some lingering questions: Trait inheritanceI am thinking that trait inheritance creates more issues than adds more value. Mainly in following situation: Currently, Sequence trait is subclass of FrameRanged and Handles. Important bit is, that class FrameRanged:
frames_per_second: float
...
class Sequence(FrameRanged):
...
representation = Representation(name="test", traits=[
Sequence(...),
])
# I need to get fps from representation
representation.contain_trait(FrameRanged) # fails
for trait in representation.get_traits().values:The other approach I like even less is: iterate over all traits on representation, find first that defines So if there is no inheritance, I can check for specific types and be clear about that, but the tradeoff is that I still need to track what Trait defines that property - which is IMO more straightforward than tracking this AND inheritance. File sequenceAnother question is description of file sequence. There are two ways:
# sequence of files
representation = Representation(name="foo", traits=[
FileLocation(Path("/foo/bar.1001.exr", ...),
Sequence(frame_start=1001, frame_end=1050, ...)
])
# single file (not sequence of one frame)
representation = Representation(name="foo", traits=[
FileLocation(Path("/foo/bar.1001.exr", ...),
Static()
])Therefor anyone working with such representation needs to check for
representation = Representation(name="foo", traits=[
FileLocations(paths=[
Path("/foo/bar.1001.exr"),
Path("/foo/bar.1002.exr"),
...
]),
Sequence(frame_start=1001, frame_end=1050, ...)
])With this, we can track explicitly all individual files, we don't need to do all those calculations of possible frame padding, etc. and the traits can use |
| FrameRanged: FrameRanged trait. | ||
|
|
||
| """ | ||
| col = assemble([path.as_posix() for path in paths])[0][0] |
There was a problem hiding this comment.
This will error on:
- File paths that do not have a sequence identifier
- File paths that only contain a single frame
Because both won'tbe detected as collections but as remainder
There was a problem hiding this comment.
I think that this function is potential danger, because it sounds like it can be used for any case, but it causing the same restriction we have now.
There was a problem hiding this comment.
Main use of this function is on FrameRanged trait validation that should be sequence by default and if it fails then it's just right thing to do. But maybe it should be better annotated then.
There was a problem hiding this comment.
Pull Request Overview
This PR introduces a new representation model defined by traits that makes data handling more flexible, strictly typed, and developer friendly. Key changes include:
- New trait implementations spanning content, time, two-/three-dimensional, cryptography, and lifecycle domains.
- A comprehensive set of tests to validate trait functionality and representation behavior.
- Updates to configuration files (pyproject.toml, addon interfaces) to support improved linting, type checking, and addon structure.
Reviewed Changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/client/ayon_core/pipeline/traits/test_two_dimesional_traits.py | New tests for 2D traits including UDIM and file location functionality. |
| tests/client/ayon_core/pipeline/traits/test_traits.py | Tests ensuring representation and trait equality, addition, and removal work as expected. |
| tests/client/ayon_core/pipeline/traits/test_time_traits.py | Validation tests for time-related traits and frame range parsing. |
| tests/client/ayon_core/pipeline/traits/test_content_traits.py | Tests for content traits including bundle handling and file locations validation. |
| pyproject.toml | Updates to linting (ruff, pre-commit) and type checking (mypy) configurations. |
| client/ayon_core/pipeline/traits/*.py | New trait model implementations and utility functions for representation. |
| client/ayon_core/addon/interfaces.py and init.py | Minor adjustments to addon interfaces and initialization for better type hints. |
Comments suppressed due to low confidence (1)
tests/client/ayon_core/pipeline/traits/test_two_dimesional_traits.py:1
- The file name 'test_two_dimesional_traits.py' appears to have a typo; consider renaming it to 'test_two_dimensional_traits.py' for clarity and consistency.
Filename: test_two_dimesional_traits.py
|
This was already merged in #1147 |
Changelog Description
Introducing new way of defining representations. Developer friendly, flexible yet strictly typed, easy to extend using Traits as building blocks defining what representation IS.
Additional info
New representation is holder for traits. Each trait define some properties and together with other traits describes the representation. Traits can be anything- their purpose is to help publisher, loader (and basically any other systems) to clearly identify data needed for processing. Like file system path, or frame per second, or resolution. Those properties are grouped together to logical block - frame range specific, resolution specific, etc.
This PR is introducing several new features:
TraitBase - this is a model for all Traits. It defines abstract properties like
id,name,descriptionthat needs to be in all Traits.Note
Every Trait can re-implement
validate()method. The one inTraitBasealways returnsTrue.Representationis passed to that method so every Trait can validate against all other Trait present in representation.Representation - this is "container" of sorts to hold all Traits. It holds representation
nameandid. And lot of "helper" methods to work with Traits:Most of them can run on bulk of Traits and you can access Traits by their class or by their Id. More on that below.
There is also mechanism for upgrading and versioning Traits.
Traits
There are some pre-defined Traits that should come with ayon-core to provide "common language". They are all based on
TraitBaseand are grouped to logical chunks or namespaces - content, lifecycle, meta, three_dimensional, time, two_dimensional. Their complete list can be found in the code and is obviously subject to change based on the need.Practical examples
So how to work with traits and representations? To define traits (for example in Collector plugin or in Extractor plugin:
You can work with Traits using classes, but you can also utilize their ids. That is useful when working with representation that was serialized into "plain' dictionary:
There is also feature of version upgrade on Traits. Whenever you want to de-serialize data that are using older version of trait,
upgrade()method on newer trait definition is called to reconstruct new version (downgrading isn't possible). So, you can have serialized trait like so (type trait without properties):{ ayon.2d.Image.v1: {} }But your current runtime has Image trait
ayon.2d.Image.v2that is also adding propertyfoo.Whenever you run
representation.get_trait_by_id("ayon.2d.Image")without version specifier, it can try to find out the latest Trait definition available and if it differs -v1 != v2it tries to callupgrade()method on the latest trait.Notes
Traits are no longer Pydantic models because Pydantic 2 is based on rust and we would need to support it in all possible host (distribute it somehow in AYON too). So until this issue is resolved, it is better to have them as pure Python dataclasses.