mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 12:54:40 +01:00
Merge pull request #1414 from ynput/enhancement/abstract-host-for-interfaces
Chore: Abstract host for interfaces
This commit is contained in:
commit
e4e8e7d520
12 changed files with 165 additions and 74 deletions
|
|
@ -1,6 +1,8 @@
|
|||
from .constants import ContextChangeReason
|
||||
from .abstract import AbstractHost
|
||||
from .host import (
|
||||
HostBase,
|
||||
ContextChangeData,
|
||||
)
|
||||
|
||||
from .interfaces import (
|
||||
|
|
@ -18,7 +20,10 @@ from .dirmap import HostDirmap
|
|||
__all__ = (
|
||||
"ContextChangeReason",
|
||||
|
||||
"AbstractHost",
|
||||
|
||||
"HostBase",
|
||||
"ContextChangeData",
|
||||
|
||||
"IWorkfileHost",
|
||||
"WorkfileInfo",
|
||||
|
|
|
|||
96
client/ayon_core/host/abstract.py
Normal file
96
client/ayon_core/host/abstract.py
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from abc import ABC, abstractmethod
|
||||
import typing
|
||||
from typing import Optional, Any
|
||||
|
||||
from .constants import ContextChangeReason
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from ayon_core.pipeline import Anatomy
|
||||
|
||||
from .typing import HostContextData
|
||||
|
||||
|
||||
class AbstractHost(ABC):
|
||||
"""Abstract definition of host implementation."""
|
||||
@property
|
||||
@abstractmethod
|
||||
def log(self) -> logging.Logger:
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def name(self) -> str:
|
||||
"""Host name."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_current_context(self) -> HostContextData:
|
||||
"""Get the current context of the host.
|
||||
|
||||
Current context is defined by project name, folder path and task name.
|
||||
|
||||
Returns:
|
||||
HostContextData: The current context of the host.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
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:
|
||||
"""Change context of the host.
|
||||
|
||||
Args:
|
||||
folder_entity (dict[str, Any]): Folder entity.
|
||||
task_entity (dict[str, Any]): Task entity.
|
||||
reason (ContextChangeReason): Reason for change.
|
||||
project_entity (dict[str, Any]): Project entity.
|
||||
anatomy (Anatomy): Anatomy entity.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_current_project_name(self) -> str:
|
||||
"""Get the current project name.
|
||||
|
||||
Returns:
|
||||
Optional[str]: The current project name.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_current_folder_path(self) -> Optional[str]:
|
||||
"""Get the current folder path.
|
||||
|
||||
Returns:
|
||||
Optional[str]: The current folder path.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_current_task_name(self) -> Optional[str]:
|
||||
"""Get the current task name.
|
||||
|
||||
Returns:
|
||||
Optional[str]: The current task name.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_context_title(self) -> str:
|
||||
"""Get the context title used in UIs."""
|
||||
pass
|
||||
|
|
@ -3,26 +3,21 @@ from __future__ import annotations
|
|||
import os
|
||||
import logging
|
||||
import contextlib
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
import typing
|
||||
from typing import Optional, Any
|
||||
from dataclasses import dataclass
|
||||
|
||||
import ayon_api
|
||||
|
||||
from ayon_core.lib import emit_event
|
||||
|
||||
from .constants import ContextChangeReason
|
||||
from .abstract import AbstractHost
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from ayon_core.pipeline import Anatomy
|
||||
|
||||
from typing import TypedDict
|
||||
|
||||
class HostContextData(TypedDict):
|
||||
project_name: str
|
||||
folder_path: Optional[str]
|
||||
task_name: Optional[str]
|
||||
from .typing import HostContextData
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
@ -34,7 +29,7 @@ class ContextChangeData:
|
|||
anatomy: Anatomy
|
||||
|
||||
|
||||
class HostBase(ABC):
|
||||
class HostBase(AbstractHost):
|
||||
"""Base of host implementation class.
|
||||
|
||||
Host is pipeline implementation of DCC application. This class should help
|
||||
|
|
@ -109,48 +104,41 @@ class HostBase(ABC):
|
|||
|
||||
It is called automatically when 'ayon_core.pipeline.install_host' is
|
||||
triggered.
|
||||
"""
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
@property
|
||||
def log(self):
|
||||
def log(self) -> logging.Logger:
|
||||
if self._log is None:
|
||||
self._log = logging.getLogger(self.__class__.__name__)
|
||||
return self._log
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def name(self) -> str:
|
||||
"""Host name."""
|
||||
|
||||
pass
|
||||
|
||||
def get_current_project_name(self):
|
||||
def get_current_project_name(self) -> str:
|
||||
"""
|
||||
Returns:
|
||||
Union[str, None]: Current project name.
|
||||
"""
|
||||
str: Current project name.
|
||||
|
||||
return os.environ.get("AYON_PROJECT_NAME")
|
||||
"""
|
||||
return os.environ["AYON_PROJECT_NAME"]
|
||||
|
||||
def get_current_folder_path(self) -> Optional[str]:
|
||||
"""
|
||||
Returns:
|
||||
Union[str, None]: Current asset name.
|
||||
"""
|
||||
Optional[str]: Current asset name.
|
||||
|
||||
"""
|
||||
return os.environ.get("AYON_FOLDER_PATH")
|
||||
|
||||
def get_current_task_name(self) -> Optional[str]:
|
||||
"""
|
||||
Returns:
|
||||
Union[str, None]: Current task name.
|
||||
"""
|
||||
Optional[str]: Current task name.
|
||||
|
||||
"""
|
||||
return os.environ.get("AYON_TASK_NAME")
|
||||
|
||||
def get_current_context(self) -> "HostContextData":
|
||||
def get_current_context(self) -> HostContextData:
|
||||
"""Get current context information.
|
||||
|
||||
This method should be used to get current context of host. Usage of
|
||||
|
|
@ -159,10 +147,10 @@ class HostBase(ABC):
|
|||
can't be caught properly.
|
||||
|
||||
Returns:
|
||||
Dict[str, Union[str, None]]: Context with 3 keys 'project_name',
|
||||
'folder_path' and 'task_name'. All of them can be 'None'.
|
||||
"""
|
||||
HostContextData: Current context with 'project_name',
|
||||
'folder_path' and 'task_name'.
|
||||
|
||||
"""
|
||||
return {
|
||||
"project_name": self.get_current_project_name(),
|
||||
"folder_path": self.get_current_folder_path(),
|
||||
|
|
@ -177,7 +165,7 @@ class HostBase(ABC):
|
|||
reason: ContextChangeReason = ContextChangeReason.undefined,
|
||||
project_entity: Optional[dict[str, Any]] = None,
|
||||
anatomy: Optional[Anatomy] = None,
|
||||
) -> "HostContextData":
|
||||
) -> HostContextData:
|
||||
"""Set current context information.
|
||||
|
||||
This method should be used to set current context of host. Usage of
|
||||
|
|
@ -290,7 +278,7 @@ class HostBase(ABC):
|
|||
project_name: str,
|
||||
folder_path: Optional[str],
|
||||
task_name: Optional[str],
|
||||
) -> "HostContextData":
|
||||
) -> HostContextData:
|
||||
"""Emit context change event.
|
||||
|
||||
Args:
|
||||
|
|
@ -302,7 +290,7 @@ class HostBase(ABC):
|
|||
HostContextData: Data send to context change event.
|
||||
|
||||
"""
|
||||
data = {
|
||||
data: HostContextData = {
|
||||
"project_name": project_name,
|
||||
"folder_path": folder_path,
|
||||
"task_name": task_name,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
from abc import abstractmethod
|
||||
|
||||
from ayon_core.host.abstract import AbstractHost
|
||||
|
||||
from .exceptions import MissingMethodsError
|
||||
|
||||
|
||||
class ILoadHost:
|
||||
class ILoadHost(AbstractHost):
|
||||
"""Implementation requirements to be able use reference of representations.
|
||||
|
||||
The load plugins can do referencing even without implementation of methods
|
||||
|
|
@ -24,7 +26,7 @@ class ILoadHost:
|
|||
loading. Checks only existence of methods.
|
||||
|
||||
Args:
|
||||
Union[ModuleType, HostBase]: Object of host where to look for
|
||||
Union[ModuleType, AbstractHost]: Object of host where to look for
|
||||
required methods.
|
||||
|
||||
Returns:
|
||||
|
|
@ -46,7 +48,7 @@ class ILoadHost:
|
|||
"""Validate implemented methods of "old type" host for load workflow.
|
||||
|
||||
Args:
|
||||
Union[ModuleType, HostBase]: Object of host to validate.
|
||||
Union[ModuleType, AbstractHost]: Object of host to validate.
|
||||
|
||||
Raises:
|
||||
MissingMethodsError: If there are missing methods on host
|
||||
|
|
@ -83,7 +85,7 @@ class ILoadHost:
|
|||
return self.get_containers()
|
||||
|
||||
|
||||
class IPublishHost:
|
||||
class IPublishHost(AbstractHost):
|
||||
"""Functions related to new creation system in new publisher.
|
||||
|
||||
New publisher is not storing information only about each created instance
|
||||
|
|
@ -99,7 +101,7 @@ class IPublishHost:
|
|||
new publish creation. Checks only existence of methods.
|
||||
|
||||
Args:
|
||||
Union[ModuleType, HostBase]: Host module where to look for
|
||||
Union[ModuleType, AbstractHost]: Host module where to look for
|
||||
required methods.
|
||||
|
||||
Returns:
|
||||
|
|
@ -127,7 +129,7 @@ class IPublishHost:
|
|||
"""Validate implemented methods of "old type" host.
|
||||
|
||||
Args:
|
||||
Union[ModuleType, HostBase]: Host module to validate.
|
||||
Union[ModuleType, AbstractHost]: Host module to validate.
|
||||
|
||||
Raises:
|
||||
MissingMethodsError: If there are missing methods on host
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import arrow
|
|||
|
||||
from ayon_core.lib import emit_event
|
||||
from ayon_core.settings import get_project_settings
|
||||
from ayon_core.host.abstract import AbstractHost
|
||||
from ayon_core.host.constants import ContextChangeReason
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
|
|
@ -821,7 +822,7 @@ class PublishedWorkfileInfo:
|
|||
return PublishedWorkfileInfo(**data)
|
||||
|
||||
|
||||
class IWorkfileHost:
|
||||
class IWorkfileHost(AbstractHost):
|
||||
"""Implementation requirements to be able to use workfiles utils and tool.
|
||||
|
||||
Some of the methods are pre-implemented as they generally do the same in
|
||||
|
|
|
|||
7
client/ayon_core/host/typing.py
Normal file
7
client/ayon_core/host/typing.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
from typing import Optional, TypedDict
|
||||
|
||||
|
||||
class HostContextData(TypedDict):
|
||||
project_name: str
|
||||
folder_path: Optional[str]
|
||||
task_name: Optional[str]
|
||||
|
|
@ -13,7 +13,7 @@ import pyblish.api
|
|||
from pyblish.lib import MessageHandler
|
||||
|
||||
from ayon_core import AYON_CORE_ROOT
|
||||
from ayon_core.host import HostBase
|
||||
from ayon_core.host import AbstractHost
|
||||
from ayon_core.lib import (
|
||||
is_in_tests,
|
||||
initialize_ayon_connection,
|
||||
|
|
@ -100,16 +100,16 @@ def registered_root():
|
|||
return _registered_root["_"]
|
||||
|
||||
|
||||
def install_host(host: HostBase) -> None:
|
||||
def install_host(host: AbstractHost) -> None:
|
||||
"""Install `host` into the running Python session.
|
||||
|
||||
Args:
|
||||
host (HostBase): A host interface object.
|
||||
host (AbstractHost): A host interface object.
|
||||
|
||||
"""
|
||||
if not isinstance(host, HostBase):
|
||||
if not isinstance(host, AbstractHost):
|
||||
log.error(
|
||||
f"Host must be a subclass of 'HostBase', got '{type(host)}'."
|
||||
f"Host must be a subclass of 'AbstractHost', got '{type(host)}'."
|
||||
)
|
||||
|
||||
global _is_installed
|
||||
|
|
@ -310,7 +310,7 @@ def get_current_host_name():
|
|||
"""
|
||||
|
||||
host = registered_host()
|
||||
if isinstance(host, HostBase):
|
||||
if isinstance(host, AbstractHost):
|
||||
return host.name
|
||||
return os.environ.get("AYON_HOST_NAME")
|
||||
|
||||
|
|
@ -346,28 +346,28 @@ def get_global_context():
|
|||
|
||||
def get_current_context():
|
||||
host = registered_host()
|
||||
if isinstance(host, HostBase):
|
||||
if isinstance(host, AbstractHost):
|
||||
return host.get_current_context()
|
||||
return get_global_context()
|
||||
|
||||
|
||||
def get_current_project_name():
|
||||
host = registered_host()
|
||||
if isinstance(host, HostBase):
|
||||
if isinstance(host, AbstractHost):
|
||||
return host.get_current_project_name()
|
||||
return get_global_context()["project_name"]
|
||||
|
||||
|
||||
def get_current_folder_path():
|
||||
host = registered_host()
|
||||
if isinstance(host, HostBase):
|
||||
if isinstance(host, AbstractHost):
|
||||
return host.get_current_folder_path()
|
||||
return get_global_context()["folder_path"]
|
||||
|
||||
|
||||
def get_current_task_name():
|
||||
host = registered_host()
|
||||
if isinstance(host, HostBase):
|
||||
if isinstance(host, AbstractHost):
|
||||
return host.get_current_task_name()
|
||||
return get_global_context()["task_name"]
|
||||
|
||||
|
|
|
|||
|
|
@ -54,15 +54,12 @@ from .creator_plugins import (
|
|||
discover_convertor_plugins,
|
||||
)
|
||||
if typing.TYPE_CHECKING:
|
||||
from ayon_core.host import HostBase
|
||||
from ayon_core.lib import AbstractAttrDef
|
||||
from ayon_core.lib.events import EventCallback, Event
|
||||
|
||||
from .structures import CreatedInstance
|
||||
from .creator_plugins import BaseCreator
|
||||
|
||||
class PublishHost(HostBase, IPublishHost):
|
||||
pass
|
||||
|
||||
# Import of functions and classes that were moved to different file
|
||||
# TODO Should be removed in future release - Added 24/08/28, 0.4.3-dev.1
|
||||
|
|
@ -169,7 +166,7 @@ class CreateContext:
|
|||
context which should be handled by host.
|
||||
|
||||
Args:
|
||||
host (PublishHost): Host implementation which handles implementation
|
||||
host (IPublishHost): Host implementation which handles implementation
|
||||
and global metadata.
|
||||
headless (bool): Context is created out of UI (Current not used).
|
||||
reset (bool): Reset context on initialization.
|
||||
|
|
@ -179,7 +176,7 @@ class CreateContext:
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
host: "PublishHost",
|
||||
host: IPublishHost,
|
||||
headless: bool = False,
|
||||
reset: bool = True,
|
||||
discover_publish_plugins: bool = True,
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ from ayon_api import (
|
|||
)
|
||||
|
||||
from ayon_core.settings import get_project_settings
|
||||
from ayon_core.host import IWorkfileHost, HostBase
|
||||
from ayon_core.host import IWorkfileHost, AbstractHost
|
||||
from ayon_core.lib import (
|
||||
Logger,
|
||||
StringTemplate,
|
||||
|
|
@ -126,14 +126,14 @@ class AbstractTemplateBuilder(ABC):
|
|||
placeholder population.
|
||||
|
||||
Args:
|
||||
host (Union[HostBase, ModuleType]): Implementation of host.
|
||||
host (Union[AbstractHost, ModuleType]): Implementation of host.
|
||||
"""
|
||||
|
||||
_log = None
|
||||
|
||||
def __init__(self, host):
|
||||
# Get host name
|
||||
if isinstance(host, HostBase):
|
||||
if isinstance(host, AbstractHost):
|
||||
host_name = host.name
|
||||
else:
|
||||
host_name = os.environ.get("AYON_HOST_NAME")
|
||||
|
|
@ -161,24 +161,24 @@ class AbstractTemplateBuilder(ABC):
|
|||
|
||||
@property
|
||||
def project_name(self):
|
||||
if isinstance(self._host, HostBase):
|
||||
if isinstance(self._host, AbstractHost):
|
||||
return self._host.get_current_project_name()
|
||||
return os.getenv("AYON_PROJECT_NAME")
|
||||
|
||||
@property
|
||||
def current_folder_path(self):
|
||||
if isinstance(self._host, HostBase):
|
||||
if isinstance(self._host, AbstractHost):
|
||||
return self._host.get_current_folder_path()
|
||||
return os.getenv("AYON_FOLDER_PATH")
|
||||
|
||||
@property
|
||||
def current_task_name(self):
|
||||
if isinstance(self._host, HostBase):
|
||||
if isinstance(self._host, AbstractHost):
|
||||
return self._host.get_current_task_name()
|
||||
return os.getenv("AYON_TASK_NAME")
|
||||
|
||||
def get_current_context(self):
|
||||
if isinstance(self._host, HostBase):
|
||||
if isinstance(self._host, AbstractHost):
|
||||
return self._host.get_current_context()
|
||||
return {
|
||||
"project_name": self.project_name,
|
||||
|
|
@ -254,7 +254,7 @@ class AbstractTemplateBuilder(ABC):
|
|||
"""Access to host implementation.
|
||||
|
||||
Returns:
|
||||
Union[HostBase, ModuleType]: Implementation of host.
|
||||
Union[AbstractHost, ModuleType]: Implementation of host.
|
||||
"""
|
||||
|
||||
return self._host
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ from typing import (
|
|||
)
|
||||
|
||||
from ayon_core.lib import AbstractAttrDef
|
||||
from ayon_core.host import HostBase
|
||||
from ayon_core.host import AbstractHost
|
||||
from ayon_core.pipeline.create import (
|
||||
CreateContext,
|
||||
ConvertorItem,
|
||||
|
|
@ -176,7 +176,7 @@ class AbstractPublisherBackend(AbstractPublisherCommon):
|
|||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_host(self) -> HostBase:
|
||||
def get_host(self) -> AbstractHost:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import ayon_api
|
||||
|
||||
from ayon_core.lib.events import QueuedEventSystem
|
||||
from ayon_core.host import HostBase
|
||||
from ayon_core.host import ILoadHost
|
||||
from ayon_core.pipeline import (
|
||||
registered_host,
|
||||
get_current_context,
|
||||
|
|
@ -35,7 +35,7 @@ class SceneInventoryController:
|
|||
self._projects_model = ProjectsModel(self)
|
||||
self._event_system = self._create_event_system()
|
||||
|
||||
def get_host(self) -> HostBase:
|
||||
def get_host(self) -> ILoadHost:
|
||||
return self._host
|
||||
|
||||
def emit_event(self, topic, data=None, source=None):
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ from ayon_core.lib import (
|
|||
Logger,
|
||||
)
|
||||
from ayon_core.host import (
|
||||
HostBase,
|
||||
IWorkfileHost,
|
||||
WorkfileInfo,
|
||||
PublishedWorkfileInfo,
|
||||
|
|
@ -49,19 +48,15 @@ if typing.TYPE_CHECKING:
|
|||
_NOT_SET = object()
|
||||
|
||||
|
||||
class HostType(HostBase, IWorkfileHost):
|
||||
pass
|
||||
|
||||
|
||||
class WorkfilesModel:
|
||||
"""Workfiles model."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
host: HostType,
|
||||
host: IWorkfileHost,
|
||||
controller: AbstractWorkfilesBackend
|
||||
):
|
||||
self._host: HostType = host
|
||||
self._host: IWorkfileHost = host
|
||||
self._controller: AbstractWorkfilesBackend = controller
|
||||
|
||||
self._log = Logger.get_logger("WorkfilesModel")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue