Skip to content

Workfiles: Add basic workfiles api #1275

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 120 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
120 commits
Select commit Hold shift + click to select a range
3bfb11d
moved interfaces to subfolder
iLLiCiTiT Apr 25, 2025
28eaf12
move exception into separate file
iLLiCiTiT Apr 25, 2025
9d0d830
move workfiles interface to separate file
iLLiCiTiT Apr 25, 2025
9e1e36c
remove ABC form 'IWorkfileHost'
iLLiCiTiT Apr 25, 2025
7e5d861
remove validation of methods
iLLiCiTiT Apr 25, 2025
9140d11
added helper data structure for colleting workfiles
iLLiCiTiT Apr 25, 2025
156eb14
remove unnecessary 'work_root'
iLLiCiTiT Apr 25, 2025
fe28391
add new line char at the end
iLLiCiTiT Apr 25, 2025
515cd79
first implementation of list workfiles
iLLiCiTiT Apr 25, 2025
152211a
'get_workfile_extensions' is not abstract anymore
iLLiCiTiT Apr 28, 2025
d78e25c
added helper function to collect comments from existing workfiles
iLLiCiTiT Apr 28, 2025
615529f
added 'WorkfileInfo' to host public api
iLLiCiTiT Apr 28, 2025
a61a94d
added more helper functions to workfile path mapping
iLLiCiTiT Apr 28, 2025
b5f8997
selection cares about more information
iLLiCiTiT Apr 28, 2025
1037776
pass project settings to template key getter
iLLiCiTiT Apr 28, 2025
4220f92
pass host name to template data getter
iLLiCiTiT Apr 28, 2025
67f478d
modified controller base
iLLiCiTiT Apr 28, 2025
ea12998
use only IWorkfileHost methods
iLLiCiTiT Apr 28, 2025
f5c8f01
pass host to workfiles model
iLLiCiTiT Apr 28, 2025
326a182
updated 'set_selected_workfile_path'
iLLiCiTiT Apr 28, 2025
ad7b2c4
more methods requiring 'IWorkfileHost'
iLLiCiTiT Apr 28, 2025
ec579ca
updated controller arguments to match needs
iLLiCiTiT Apr 28, 2025
16b29a6
added reset to workfile model
iLLiCiTiT Apr 28, 2025
085f4cb
added more cache items
iLLiCiTiT Apr 28, 2025
1211a71
move private methods below public ones
iLLiCiTiT Apr 28, 2025
a2dad64
move public methods above private
iLLiCiTiT Apr 28, 2025
0f64ab1
remove unused method
iLLiCiTiT Apr 28, 2025
5fe625a
remove more unnecessary methods
iLLiCiTiT Apr 28, 2025
f4961bc
updated workarea model
iLLiCiTiT Apr 28, 2025
98acfd8
updated entities model
iLLiCiTiT Apr 28, 2025
60c2c4e
move public method above private
iLLiCiTiT Apr 28, 2025
e4f6342
implement 'get_workfile_entities' on controller
iLLiCiTiT Apr 28, 2025
b8b012d
updated UI to work with new methods and structures
iLLiCiTiT Apr 28, 2025
e705358
fix abstract property
iLLiCiTiT Apr 28, 2025
95b1820
added some typehints into IWorkfileHost
iLLiCiTiT Apr 28, 2025
bef56a5
added todos into controller
iLLiCiTiT Apr 28, 2025
552bc03
added comment
iLLiCiTiT Apr 28, 2025
80397a3
implemented base of published workfile collection
iLLiCiTiT Apr 29, 2025
dde5c6a
use collect published files from host
iLLiCiTiT Apr 30, 2025
b76ae8f
update controller
iLLiCiTiT Apr 30, 2025
1b7474b
Merge workfile entities model into workfiles model
iLLiCiTiT Apr 30, 2025
2f6ed06
Remove publish workfiles model
iLLiCiTiT Apr 30, 2025
eb33281
merge workfile models into one
iLLiCiTiT Apr 30, 2025
6a7f41f
move workfiles related logic to workfiles model
iLLiCiTiT Apr 30, 2025
21c1a8b
added base implementation to workfiles interface
iLLiCiTiT May 16, 2025
29d824d
modified change current context function
iLLiCiTiT May 16, 2025
f4638b9
implemented utils functions for workfiles
iLLiCiTiT May 16, 2025
6390879
modified workfiles tool accordingly
iLLiCiTiT May 16, 2025
723463d
use correct function
iLLiCiTiT May 16, 2025
a37c074
expect entities instead of ids
iLLiCiTiT May 19, 2025
36aae5a
Merge branch 'develop' into enhancement/enhance-workfile-api
iLLiCiTiT May 19, 2025
70f3c05
linting fixes
iLLiCiTiT May 19, 2025
a159e02
fix arguments
iLLiCiTiT May 21, 2025
3d2d786
Merge branch 'develop' into enhancement/enhance-workfile-api
iLLiCiTiT May 21, 2025
edb371f
Remove outdated todo
iLLiCiTiT May 22, 2025
88647ee
Merge branch 'develop' into enhancement/enhance-workfile-api
MustafaJafar May 26, 2025
e97f7f1
added docstring to dataclasses
iLLiCiTiT May 26, 2025
c83bae2
move context change responsibility to host
iLLiCiTiT Jun 3, 2025
a879d11
get_ayon_username is using cached values
iLLiCiTiT Jun 3, 2025
8bda7dd
move all the responsibility about workfiles to IWorkfileHost
iLLiCiTiT Jun 3, 2025
c52f300
use host methods to work with workfiles and add helper functions
iLLiCiTiT Jun 3, 2025
e08c5f2
modified workfiles model to use api functions
iLLiCiTiT Jun 3, 2025
c5cd53b
Merge branch 'develop' into enhancement/enhance-workfile-api
iLLiCiTiT Jun 3, 2025
406f43a
formatting cleanup
iLLiCiTiT Jun 3, 2025
7cb22fb
add arrow to dependencies
iLLiCiTiT Jun 3, 2025
df55a32
fix 'PublishedWorkfileInfo'
iLLiCiTiT Jun 3, 2025
f4545a6
some formatting changes
iLLiCiTiT Jun 3, 2025
e6bb395
set AYON_WORKDIR when workfile is opened
iLLiCiTiT Jun 3, 2025
524ed03
removed unnecessary workdir handling from set current context
iLLiCiTiT Jun 3, 2025
63b69a9
added 'TemplateResult' typint
iLLiCiTiT Jun 3, 2025
0f79217
remove unused imports
iLLiCiTiT Jun 3, 2025
87832f1
modified change context function
iLLiCiTiT Jun 3, 2025
77dbf29
added typehints and modify docstrings
iLLiCiTiT Jun 3, 2025
6391116
add deprecation warning for 'full_path' argument
iLLiCiTiT Jun 3, 2025
c8a7e35
iterate over extensions fix
iLLiCiTiT Jun 3, 2025
f130f54
add missing import
iLLiCiTiT Jun 3, 2025
79922d9
update docstrings
iLLiCiTiT Jun 3, 2025
dd637bb
use comprehention
iLLiCiTiT Jun 3, 2025
808712e
Apply suggestions from code review
iLLiCiTiT Jun 4, 2025
5baf13c
fix formatting
iLLiCiTiT Jun 4, 2025
509543e
better description of workfile_entities
iLLiCiTiT Jun 4, 2025
3701564
Merge branch 'develop' into enhancement/enhance-workfile-api
iLLiCiTiT Jun 4, 2025
26145b3
Merge branch 'develop' into enhancement/enhance-workfile-api
iLLiCiTiT Jun 5, 2025
e53962d
let 'version' argument optional
iLLiCiTiT Jun 6, 2025
d97829a
filter published workfiles by extension
iLLiCiTiT Jun 6, 2025
26a35a8
pre-fetch project entity
iLLiCiTiT Jun 6, 2025
d3699c3
modified docstrings
iLLiCiTiT Jun 6, 2025
76ceefb
require kwargs for 'list_workfiles'
iLLiCiTiT Jun 6, 2025
3e6aafa
apply suggestion
iLLiCiTiT Jun 6, 2025
351ab7d
use 'open_workfile' to open workfile
iLLiCiTiT Jun 6, 2025
762f986
use dataclasses to pass information form method to method
iLLiCiTiT Jun 6, 2025
688e5f2
remove unnecessary line
iLLiCiTiT Jun 6, 2025
da1a39e
validate extension earlier
iLLiCiTiT Jun 6, 2025
411c433
added typehints
iLLiCiTiT Jun 6, 2025
0c25def
added more docstrings
iLLiCiTiT Jun 6, 2025
397bfd2
added deprecation warnings
iLLiCiTiT Jun 6, 2025
7eb067a
remove safe type hint
iLLiCiTiT Jun 6, 2025
a23678b
use 'ContextChangeData' for '_set_current_context'
iLLiCiTiT Jun 6, 2025
873db37
don't use safe typehint
iLLiCiTiT Jun 6, 2025
0bf1e9a
add indentation
iLLiCiTiT Jun 12, 2025
e3114d8
Apply suggestions from code review
iLLiCiTiT Jun 12, 2025
2e798f9
Apply suggestions from code review
iLLiCiTiT Jun 16, 2025
34d9289
remove invalid returns typehints
iLLiCiTiT Jun 16, 2025
09858f6
added typeddict for context data
iLLiCiTiT Jun 16, 2025
7666586
use kwargs
iLLiCiTiT Jun 17, 2025
0ae72c8
small enhancmement of docstring
iLLiCiTiT Jun 17, 2025
48bc7a0
small clarity enhancement
iLLiCiTiT Jun 17, 2025
8b35eb3
fix kwargs
iLLiCiTiT Jun 18, 2025
3e5e873
added helper function to save current file with current context
iLLiCiTiT Jun 18, 2025
060fea4
Merge branch 'develop' into enhancement/enhance-workfile-api
iLLiCiTiT Jun 18, 2025
ab363bf
remove typehint
iLLiCiTiT Jun 23, 2025
1e7c9db
define context change reason enum
iLLiCiTiT Jun 26, 2025
1d40243
fix typehint
iLLiCiTiT Jun 26, 2025
646f3be
wrap optional arguments into wrappers
iLLiCiTiT Jun 26, 2025
91377aa
formatting fixes
iLLiCiTiT Jun 26, 2025
3e2c6f7
Merge branch 'develop' into enhancement/enhance-workfile-api
iLLiCiTiT Jun 26, 2025
dc476de
fix missing argument
iLLiCiTiT Jun 26, 2025
b142bc4
expect folder path and task name in 'save_current_workfile_to'
iLLiCiTiT Jul 2, 2025
bc9a1b6
remove unused import
iLLiCiTiT Jul 2, 2025
a29ebd0
Merge branch 'develop' into enhancement/enhance-workfile-api
iLLiCiTiT Jul 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions client/ayon_core/host/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from .constants import ContextChangeReason
from .host import (
HostBase,
)

from .interfaces import (
IWorkfileHost,
WorkfileInfo,
PublishedWorkfileInfo,
ILoadHost,
IPublishHost,
INewPublisher,
Expand All @@ -13,9 +16,13 @@


__all__ = (
"ContextChangeReason",

"HostBase",

"IWorkfileHost",
"WorkfileInfo",
"PublishedWorkfileInfo",
"ILoadHost",
"IPublishHost",
"INewPublisher",
Expand Down
15 changes: 15 additions & 0 deletions client/ayon_core/host/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from enum import Enum


class StrEnum(str, Enum):
"""A string-based Enum class that allows for string comparison."""

def __str__(self) -> str:
return self.value


class ContextChangeReason(StrEnum):
"""Reasons for context change in the host."""
undefined = "undefined"
workfile_open = "workfile.opened"
workfile_save = "workfile.saved"
201 changes: 193 additions & 8 deletions client/ayon_core/host/host.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,37 @@
from __future__ import annotations

import os
import logging
import contextlib
from abc import ABC, abstractproperty
from abc import ABC, abstractmethod
from dataclasses import dataclass
import typing
from typing import Optional, Any

import ayon_api

from ayon_core.lib import emit_event

from .constants import ContextChangeReason

if typing.TYPE_CHECKING:
from ayon_core.pipeline import Anatomy

from typing import TypedDict

# NOTE can't import 'typing' because of issues in Maya 2020
# - shiboken crashes on 'typing' module import
class HostContextData(TypedDict):
project_name: str
folder_path: Optional[str]
task_name: Optional[str]


@dataclass
class ContextChangeData:
project_entity: dict[str, Any]
folder_entity: dict[str, Any]
task_entity: dict[str, Any]
reason: ContextChangeReason
anatomy: Anatomy


class HostBase(ABC):
Expand Down Expand Up @@ -92,8 +119,9 @@ def log(self):
self._log = logging.getLogger(self.__class__.__name__)
return self._log

@abstractproperty
def name(self):
@property
@abstractmethod
def name(self) -> str:
"""Host name."""

pass
Expand All @@ -106,23 +134,23 @@ def get_current_project_name(self):

return os.environ.get("AYON_PROJECT_NAME")

def get_current_folder_path(self):
def get_current_folder_path(self) -> Optional[str]:
"""
Returns:
Union[str, None]: Current asset name.
"""

return os.environ.get("AYON_FOLDER_PATH")

def get_current_task_name(self):
def get_current_task_name(self) -> Optional[str]:
"""
Returns:
Union[str, None]: Current task name.
"""

return os.environ.get("AYON_TASK_NAME")

def get_current_context(self):
def get_current_context(self) -> "HostContextData":
"""Get current context information.

This method should be used to get current context of host. Usage of
Expand All @@ -141,6 +169,75 @@ def get_current_context(self):
"task_name": self.get_current_task_name()
}

def set_current_context(
self,
folder_entity: dict[str, Any],
task_entity: dict[str, Any],
*,
reason: ContextChangeReason = ContextChangeReason.undefined,
project_entity: Optional[dict[str, Any]] = None,
anatomy: Optional[Anatomy] = None,
) -> "HostContextData":
"""Set current context information.

This method should be used to set current context of host. Usage of
this method can be crucial for host implementations in DCCs where
can be opened multiple workfiles at one moment and change of context
can't be caught properly.

Notes:
This method should not care about change of workdir and expect any
of the arguments.

Args:
folder_entity (Optional[dict[str, Any]]): Folder entity.
task_entity (Optional[dict[str, Any]]): Task entity.
reason (ContextChangeReason): Reason for context change.
project_entity (Optional[dict[str, Any]]): Project entity data.
anatomy (Optional[Anatomy]): Anatomy instance for the project.

Returns:
dict[str, Optional[str]]: Context information with project name,
folder path and task name.

"""
from ayon_core.pipeline import Anatomy

folder_path = folder_entity["path"]
task_name = task_entity["name"]

context = self.get_current_context()
# Don't do anything if context did not change
if (
context["folder_path"] == folder_path
and context["task_name"] == task_name
):
return context

project_name = self.get_current_project_name()
if project_entity is None:
project_entity = ayon_api.get_project(project_name)

if anatomy is None:
anatomy = Anatomy(project_name, project_entity=project_entity)

context_change_data = ContextChangeData(
project_entity,
folder_entity,
task_entity,
reason,
anatomy,
)
self._before_context_change(context_change_data)
self._set_current_context(context_change_data)
self._after_context_change(context_change_data)

return self._emit_context_change_event(
project_name,
folder_path,
task_name,
)

def get_context_title(self):
"""Context title shown for UI purposes.

Expand Down Expand Up @@ -187,3 +284,91 @@ def maintained_selection(self):
yield
finally:
pass

def _emit_context_change_event(
self,
project_name: str,
folder_path: Optional[str],
task_name: Optional[str],
) -> "HostContextData":
"""Emit context change event.

Args:
project_name (str): Name of the project.
folder_path (Optional[str]): Path of the folder.
task_name (Optional[str]): Name of the task.

Returns:
HostContextData: Data send to context change event.

"""
data = {
"project_name": project_name,
"folder_path": folder_path,
"task_name": task_name,
}
emit_event("taskChanged", data)
return data

def _set_current_context(
self, context_change_data: ContextChangeData
) -> None:
"""Method that changes the context in host.

Can be overriden for hosts that do need different handling of context
than using environment variables.

Args:
context_change_data (ContextChangeData): Context change related
data.

"""
project_name = self.get_current_project_name()
folder_path = None
task_name = None
if context_change_data.folder_entity:
folder_path = context_change_data.folder_entity["path"]
if context_change_data.task_entity:
task_name = context_change_data.task_entity["name"]

envs = {
"AYON_PROJECT_NAME": project_name,
"AYON_FOLDER_PATH": folder_path,
"AYON_TASK_NAME": task_name,
}

# Update the Session and environments. Pop from environments all
# keys with value set to None.
for key, value in envs.items():
if value is None:
os.environ.pop(key, None)
else:
os.environ[key] = value

def _before_context_change(self, context_change_data: ContextChangeData):
"""Before context is changed.

This method is called before the context is changed in the host.

Can be overridden to implement host specific logic.

Args:
context_change_data (ContextChangeData): Object with information
about context change.

"""
pass

def _after_context_change(self, context_change_data: ContextChangeData):
"""After context is changed.

This method is called after the context is changed in the host.

Can be overridden to implement host specific logic.

Args:
context_change_data (ContextChangeData): Object with information
about context change.

"""
pass
66 changes: 66 additions & 0 deletions client/ayon_core/host/interfaces/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from .exceptions import MissingMethodsError
from .workfiles import (
IWorkfileHost,
WorkfileInfo,
PublishedWorkfileInfo,

OpenWorkfileOptionalData,
ListWorkfilesOptionalData,
ListPublishedWorkfilesOptionalData,
SaveWorkfileOptionalData,
CopyWorkfileOptionalData,
CopyPublishedWorkfileOptionalData,

get_open_workfile_context,
get_list_workfiles_context,
get_list_published_workfiles_context,
get_save_workfile_context,
get_copy_workfile_context,
get_copy_repre_workfile_context,

OpenWorkfileContext,
ListWorkfilesContext,
ListPublishedWorkfilesContext,
SaveWorkfileContext,
CopyWorkfileContext,
CopyPublishedWorkfileContext,
)
from .interfaces import (
IPublishHost,
INewPublisher,
ILoadHost,
)


__all__ = (
"MissingMethodsError",

"IWorkfileHost",
"WorkfileInfo",
"PublishedWorkfileInfo",

"OpenWorkfileOptionalData",
"ListWorkfilesOptionalData",
"ListPublishedWorkfilesOptionalData",
"SaveWorkfileOptionalData",
"CopyWorkfileOptionalData",
"CopyPublishedWorkfileOptionalData",

"get_open_workfile_context",
"get_list_workfiles_context",
"get_list_published_workfiles_context",
"get_save_workfile_context",
"get_copy_workfile_context",
"get_copy_repre_workfile_context",

"OpenWorkfileContext",
"ListWorkfilesContext",
"ListPublishedWorkfilesContext",
"SaveWorkfileContext",
"CopyWorkfileContext",
"CopyPublishedWorkfileContext",

"IPublishHost",
"INewPublisher",
"ILoadHost",
)
15 changes: 15 additions & 0 deletions client/ayon_core/host/interfaces/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class MissingMethodsError(ValueError):
"""Exception when host miss some required methods for a specific workflow.

Args:
host (HostBase): Host implementation where are missing methods.
missing_methods (list[str]): List of missing methods.
"""

def __init__(self, host, missing_methods):
joined_missing = ", ".join(
['"{}"'.format(item) for item in missing_methods]
)
super().__init__(
f"Host \"{host.name}\" miss methods {joined_missing}"
)
Loading
Loading