Skip to content

host

HostBase

Bases: ABC

Base of host implementation class.

Host is pipeline implementation of DCC application. This class should help to identify what must/should/can be implemented for specific functionality.

Compared to 'avalon' concept: What was before considered as functions in host implementation folder. The host implementation should primarily care about adding ability of creation (mark products to be published) and optionally about referencing published representations as containers.

Host may need extend some functionality like working with workfiles or loading. Not all host implementations may allow that for those purposes can be logic extended with implementing functions for the purpose. There are prepared interfaces to be able identify what must be implemented to be able use that functionality. - current statement is that it is not required to inherit from interfaces but all of the methods are validated (only their existence!)

Installation of host before (avalon concept):

from ayon_core.pipeline import install_host
import ayon_core.hosts.maya.api as host

install_host(host)

Installation of host now:

from ayon_core.pipeline import install_host
from ayon_core.hosts.maya.api import MayaHost

host = MayaHost()
install_host(host)
Todo
  • move content of 'install_host' as method of this class
    • register host object
    • install global plugin paths
  • store registered plugin paths to this object
  • handle current context (project, asset, task)
    • this must be done in many separated steps
  • have it's object of host tools instead of using globals

This implementation will probably change over time when more functionality and responsibility will be added.

Source code in client/ayon_core/host/host.py
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
class HostBase(ABC):
    """Base of host implementation class.

    Host is pipeline implementation of DCC application. This class should help
    to identify what must/should/can be implemented for specific functionality.

    Compared to 'avalon' concept:
    What was before considered as functions in host implementation folder. The
    host implementation should primarily care about adding ability of creation
    (mark products to be published) and optionally about referencing published
    representations as containers.

    Host may need extend some functionality like working with workfiles
    or loading. Not all host implementations may allow that for those purposes
    can be logic extended with implementing functions for the purpose. There
    are prepared interfaces to be able identify what must be implemented to
    be able use that functionality.
    - current statement is that it is not required to inherit from interfaces
        but all of the methods are validated (only their existence!)

    # Installation of host before (avalon concept):
    ```python
    from ayon_core.pipeline import install_host
    import ayon_core.hosts.maya.api as host

    install_host(host)
    ```

    # Installation of host now:
    ```python
    from ayon_core.pipeline import install_host
    from ayon_core.hosts.maya.api import MayaHost

    host = MayaHost()
    install_host(host)
    ```

    Todo:
        - move content of 'install_host' as method of this class
            - register host object
            - install global plugin paths
        - store registered plugin paths to this object
        - handle current context (project, asset, task)
            - this must be done in many separated steps
        - have it's object of host tools instead of using globals

    This implementation will probably change over time when more
        functionality and responsibility will be added.
    """

    _log = None

    def __init__(self):
        """Initialization of host.

        Register DCC callbacks, host specific plugin paths, targets etc.
        (Part of what 'install' did in 'avalon' concept.)

        Note:
            At this moment global "installation" must happen before host
            installation. Because of this current limitation it is recommended
            to implement 'install' method which is triggered after global
            'install'.
        """

        pass

    def install(self):
        """Install host specific functionality.

        This is where should be added menu with tools, registered callbacks
        and other host integration initialization.

        It is called automatically when 'ayon_core.pipeline.install_host' is
        triggered.
        """

        pass

    @property
    def log(self):
        if self._log is None:
            self._log = logging.getLogger(self.__class__.__name__)
        return self._log

    @abstractproperty
    def name(self):
        """Host name."""

        pass

    def get_current_project_name(self):
        """
        Returns:
            Union[str, None]: Current project name.
        """

        return os.environ.get("AYON_PROJECT_NAME")

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

        return os.environ.get("AYON_FOLDER_PATH")

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

        return os.environ.get("AYON_TASK_NAME")

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

        This method should be used to get 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.

        Returns:
            Dict[str, Union[str, None]]: Context with 3 keys 'project_name',
                'folder_path' and 'task_name'. All of them can be 'None'.
        """

        return {
            "project_name": self.get_current_project_name(),
            "folder_path": self.get_current_folder_path(),
            "task_name": self.get_current_task_name()
        }

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

        Should return current context title if possible.

        Note:
            This method is used only for UI purposes so it is possible to
                return some logical title for contextless cases.
            Is not meant for "Context menu" label.

        Returns:
            str: Context title.
            None: Default title is used based on UI implementation.
        """

        # Use current context to fill the context title
        current_context = self.get_current_context()
        project_name = current_context["project_name"]
        folder_path = current_context["folder_path"]
        task_name = current_context["task_name"]
        items = []
        if project_name:
            items.append(project_name)
            if folder_path:
                items.append(folder_path.lstrip("/"))
                if task_name:
                    items.append(task_name)
        if items:
            return "/".join(items)
        return None

    @contextlib.contextmanager
    def maintained_selection(self):
        """Some functionlity will happen but selection should stay same.

        This is DCC specific. Some may not allow to implement this ability
        that is reason why default implementation is empty context manager.

        Yields:
            None: Yield when is ready to restore selected at the end.
        """

        try:
            yield
        finally:
            pass

__init__()

Initialization of host.

Register DCC callbacks, host specific plugin paths, targets etc. (Part of what 'install' did in 'avalon' concept.)

Note

At this moment global "installation" must happen before host installation. Because of this current limitation it is recommended to implement 'install' method which is triggered after global 'install'.

Source code in client/ayon_core/host/host.py
62
63
64
65
66
67
68
69
70
71
72
73
74
75
def __init__(self):
    """Initialization of host.

    Register DCC callbacks, host specific plugin paths, targets etc.
    (Part of what 'install' did in 'avalon' concept.)

    Note:
        At this moment global "installation" must happen before host
        installation. Because of this current limitation it is recommended
        to implement 'install' method which is triggered after global
        'install'.
    """

    pass

get_context_title()

Context title shown for UI purposes.

Should return current context title if possible.

Note

This method is used only for UI purposes so it is possible to return some logical title for contextless cases. Is not meant for "Context menu" label.

Returns:

Name Type Description
str

Context title.

None

Default title is used based on UI implementation.

Source code in client/ayon_core/host/host.py
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
def get_context_title(self):
    """Context title shown for UI purposes.

    Should return current context title if possible.

    Note:
        This method is used only for UI purposes so it is possible to
            return some logical title for contextless cases.
        Is not meant for "Context menu" label.

    Returns:
        str: Context title.
        None: Default title is used based on UI implementation.
    """

    # Use current context to fill the context title
    current_context = self.get_current_context()
    project_name = current_context["project_name"]
    folder_path = current_context["folder_path"]
    task_name = current_context["task_name"]
    items = []
    if project_name:
        items.append(project_name)
        if folder_path:
            items.append(folder_path.lstrip("/"))
            if task_name:
                items.append(task_name)
    if items:
        return "/".join(items)
    return None

get_current_context()

Get current context information.

This method should be used to get 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.

Returns:

Type Description

Dict[str, Union[str, None]]: Context with 3 keys 'project_name', 'folder_path' and 'task_name'. All of them can be 'None'.

Source code in client/ayon_core/host/host.py
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
def get_current_context(self):
    """Get current context information.

    This method should be used to get 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.

    Returns:
        Dict[str, Union[str, None]]: Context with 3 keys 'project_name',
            'folder_path' and 'task_name'. All of them can be 'None'.
    """

    return {
        "project_name": self.get_current_project_name(),
        "folder_path": self.get_current_folder_path(),
        "task_name": self.get_current_task_name()
    }

get_current_folder_path()

Returns:

Type Description

Union[str, None]: Current asset name.

Source code in client/ayon_core/host/host.py
109
110
111
112
113
114
115
def get_current_folder_path(self):
    """
    Returns:
        Union[str, None]: Current asset name.
    """

    return os.environ.get("AYON_FOLDER_PATH")

get_current_project_name()

Returns:

Type Description

Union[str, None]: Current project name.

Source code in client/ayon_core/host/host.py
101
102
103
104
105
106
107
def get_current_project_name(self):
    """
    Returns:
        Union[str, None]: Current project name.
    """

    return os.environ.get("AYON_PROJECT_NAME")

get_current_task_name()

Returns:

Type Description

Union[str, None]: Current task name.

Source code in client/ayon_core/host/host.py
117
118
119
120
121
122
123
def get_current_task_name(self):
    """
    Returns:
        Union[str, None]: Current task name.
    """

    return os.environ.get("AYON_TASK_NAME")

install()

Install host specific functionality.

This is where should be added menu with tools, registered callbacks and other host integration initialization.

It is called automatically when 'ayon_core.pipeline.install_host' is triggered.

Source code in client/ayon_core/host/host.py
77
78
79
80
81
82
83
84
85
86
87
def install(self):
    """Install host specific functionality.

    This is where should be added menu with tools, registered callbacks
    and other host integration initialization.

    It is called automatically when 'ayon_core.pipeline.install_host' is
    triggered.
    """

    pass

maintained_selection()

Some functionlity will happen but selection should stay same.

This is DCC specific. Some may not allow to implement this ability that is reason why default implementation is empty context manager.

Yields:

Name Type Description
None

Yield when is ready to restore selected at the end.

Source code in client/ayon_core/host/host.py
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
@contextlib.contextmanager
def maintained_selection(self):
    """Some functionlity will happen but selection should stay same.

    This is DCC specific. Some may not allow to implement this ability
    that is reason why default implementation is empty context manager.

    Yields:
        None: Yield when is ready to restore selected at the end.
    """

    try:
        yield
    finally:
        pass

name()

Host name.

Source code in client/ayon_core/host/host.py
95
96
97
98
99
@abstractproperty
def name(self):
    """Host name."""

    pass

HostDirmap

Bases: ABC

Abstract class for running dirmap on a workfile in a host.

Dirmap is used to translate paths inside of host workfile from one OS to another. (Eg. arstist created workfile on Win, different artists opens same file on Linux.)

Expects methods to be implemented inside of host

on_dirmap_enabled: run host code for enabling dirmap do_dirmap: run host code to do actual remapping

Source code in client/ayon_core/host/dirmap.py
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
class HostDirmap(ABC):
    """Abstract class for running dirmap on a workfile in a host.

    Dirmap is used to translate paths inside of host workfile from one
    OS to another. (Eg. arstist created workfile on Win, different artists
    opens same file on Linux.)

    Expects methods to be implemented inside of host:
        on_dirmap_enabled: run host code for enabling dirmap
        do_dirmap: run host code to do actual remapping
    """

    def __init__(
        self,
        host_name,
        project_name,
        project_settings=None,
        sitesync_addon=None
    ):
        self.host_name = host_name
        self.project_name = project_name
        self._project_settings = project_settings
        self._sitesync_addon = sitesync_addon
        # to limit reinit of Modules
        self._sitesync_addon_discovered = sitesync_addon is not None
        self._log = None

    @property
    def sitesync_addon(self):
        if not self._sitesync_addon_discovered:
            self._sitesync_addon_discovered = True
            manager = AddonsManager()
            self._sitesync_addon = manager.get("sitesync")
        return self._sitesync_addon

    @property
    def project_settings(self):
        if self._project_settings is None:
            self._project_settings = get_project_settings(self.project_name)
        return self._project_settings

    @property
    def log(self):
        if self._log is None:
            self._log = Logger.get_logger(self.__class__.__name__)
        return self._log

    @abstractmethod
    def on_enable_dirmap(self):
        """Run host dependent operation for enabling dirmap if necessary."""
        pass

    @abstractmethod
    def dirmap_routine(self, source_path, destination_path):
        """Run host dependent remapping from source_path to destination_path"""
        pass

    def process_dirmap(self, mapping=None):
        # type: (dict) -> None
        """Go through all paths in Settings and set them using `dirmap`.

            If artists has Site Sync enabled, take dirmap mapping directly from
            Local Settings when artist is syncing workfile locally.

        """

        if not mapping:
            mapping = self.get_mappings()
        if not mapping:
            return

        self.on_enable_dirmap()

        for k, sp in enumerate(mapping["source_path"]):
            dst = mapping["destination_path"][k]
            try:
                # add trailing slash if missing
                sp = os.path.join(sp, '')
                dst = os.path.join(dst, '')
                print("{} -> {}".format(sp, dst))
                self.dirmap_routine(sp, dst)
            except IndexError:
                # missing corresponding destination path
                self.log.error((
                    "invalid dirmap mapping, missing corresponding"
                    " destination directory."
                ))
                break
            except RuntimeError:
                self.log.error(
                    "invalid path {} -> {}, mapping not registered".format(
                        sp, dst
                    )
                )
                continue

    def get_mappings(self):
        """Get translation from source_path to destination_path.

            It checks if Site Sync is enabled and user chose to use local
            site, in that case configuration in Local Settings takes precedence
        """
        mapping_sett = self.project_settings[self.host_name].get("dirmap", {})
        local_mapping = self._get_local_sync_dirmap()
        mapping_enabled = mapping_sett.get("enabled") or bool(local_mapping)
        if not mapping_enabled:
            return {}

        mapping = (
            local_mapping
            or mapping_sett["paths"]
            or {}
        )

        if (
            not mapping
            or not mapping.get("destination_path")
            or not mapping.get("source_path")
        ):
            return {}
        self.log.info("Processing directory mapping ...")
        self.log.info("mapping:: {}".format(mapping))
        return mapping

    def _get_local_sync_dirmap(self):
        """
            Returns dirmap if synch to local project is enabled.

            Only valid mapping is from roots of remote site to local site set
            in Local Settings.

            Returns:
                dict : { "source_path": [XXX], "destination_path": [YYYY]}
        """
        project_name = self.project_name

        sitesync_addon = self.sitesync_addon
        mapping = {}
        if (
            sitesync_addon is None
            or not sitesync_addon.enabled
            or not sitesync_addon.is_project_enabled(project_name, True)
        ):
            return mapping

        active_site = sitesync_addon.get_local_normalized_site(
            sitesync_addon.get_active_site(project_name))
        remote_site = sitesync_addon.get_local_normalized_site(
            sitesync_addon.get_remote_site(project_name))
        self.log.debug(
            "active {} - remote {}".format(active_site, remote_site)
        )

        if active_site == "local" and active_site != remote_site:
            sync_settings = sitesync_addon.get_sync_project_setting(
                project_name,
                exclude_locals=False,
                cached=False)

            # overrides for roots set in `Site Settings`
            active_roots_overrides = self._get_site_root_overrides(
                sitesync_addon, project_name, active_site)

            remote_roots_overrides = self._get_site_root_overrides(
                sitesync_addon, project_name, remote_site)

            current_platform = platform.system().lower()
            remote_provider = sitesync_addon.get_provider_for_site(
                project_name, remote_site
            )
            # dirmap has sense only with regular disk provider, in the workfile
            # won't be root on cloud or sftp provider so fallback to studio
            if remote_provider != "local_drive":
                remote_site = "studio"
            for root_name, active_site_dir in active_roots_overrides.items():
                remote_site_dir = (
                    remote_roots_overrides.get(root_name)
                    or sync_settings["sites"][remote_site]["root"][root_name]
                )

                if isinstance(remote_site_dir, dict):
                    remote_site_dir = remote_site_dir.get(current_platform)

                if not remote_site_dir:
                    continue

                if os.path.isdir(active_site_dir):
                    if "destination_path" not in mapping:
                        mapping["destination_path"] = []
                    mapping["destination_path"].append(active_site_dir)

                    if "source_path" not in mapping:
                        mapping["source_path"] = []
                    mapping["source_path"].append(remote_site_dir)

            self.log.debug("local sync mapping:: {}".format(mapping))
        return mapping

    def _get_site_root_overrides(
        self, sitesync_addon, project_name, site_name
    ):
        """Safely handle root overrides.

        SiteSync raises ValueError for non local or studio sites.
        """
        # TODO: could be removed when `get_site_root_overrides` is not raising
        #     an Error but just returns {}
        try:
            site_roots_overrides = sitesync_addon.get_site_root_overrides(
                project_name, site_name)
        except ValueError:
            site_roots_overrides = {}
        self.log.debug("{} roots overrides {}".format(
            site_name, site_roots_overrides))

        return site_roots_overrides

dirmap_routine(source_path, destination_path) abstractmethod

Run host dependent remapping from source_path to destination_path

Source code in client/ayon_core/host/dirmap.py
70
71
72
73
@abstractmethod
def dirmap_routine(self, source_path, destination_path):
    """Run host dependent remapping from source_path to destination_path"""
    pass

get_mappings()

Get translation from source_path to destination_path.

It checks if Site Sync is enabled and user chose to use local site, in that case configuration in Local Settings takes precedence

Source code in client/ayon_core/host/dirmap.py
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def get_mappings(self):
    """Get translation from source_path to destination_path.

        It checks if Site Sync is enabled and user chose to use local
        site, in that case configuration in Local Settings takes precedence
    """
    mapping_sett = self.project_settings[self.host_name].get("dirmap", {})
    local_mapping = self._get_local_sync_dirmap()
    mapping_enabled = mapping_sett.get("enabled") or bool(local_mapping)
    if not mapping_enabled:
        return {}

    mapping = (
        local_mapping
        or mapping_sett["paths"]
        or {}
    )

    if (
        not mapping
        or not mapping.get("destination_path")
        or not mapping.get("source_path")
    ):
        return {}
    self.log.info("Processing directory mapping ...")
    self.log.info("mapping:: {}".format(mapping))
    return mapping

on_enable_dirmap() abstractmethod

Run host dependent operation for enabling dirmap if necessary.

Source code in client/ayon_core/host/dirmap.py
65
66
67
68
@abstractmethod
def on_enable_dirmap(self):
    """Run host dependent operation for enabling dirmap if necessary."""
    pass

process_dirmap(mapping=None)

Go through all paths in Settings and set them using dirmap.

If artists has Site Sync enabled, take dirmap mapping directly from Local Settings when artist is syncing workfile locally.

Source code in client/ayon_core/host/dirmap.py
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
def process_dirmap(self, mapping=None):
    # type: (dict) -> None
    """Go through all paths in Settings and set them using `dirmap`.

        If artists has Site Sync enabled, take dirmap mapping directly from
        Local Settings when artist is syncing workfile locally.

    """

    if not mapping:
        mapping = self.get_mappings()
    if not mapping:
        return

    self.on_enable_dirmap()

    for k, sp in enumerate(mapping["source_path"]):
        dst = mapping["destination_path"][k]
        try:
            # add trailing slash if missing
            sp = os.path.join(sp, '')
            dst = os.path.join(dst, '')
            print("{} -> {}".format(sp, dst))
            self.dirmap_routine(sp, dst)
        except IndexError:
            # missing corresponding destination path
            self.log.error((
                "invalid dirmap mapping, missing corresponding"
                " destination directory."
            ))
            break
        except RuntimeError:
            self.log.error(
                "invalid path {} -> {}, mapping not registered".format(
                    sp, dst
                )
            )
            continue

ILoadHost

Implementation requirements to be able use reference of representations.

The load plugins can do referencing even without implementation of methods here, but switch and removement of containers would not be possible.

Questions
  • Is list container dependency of host or load plugins?
  • Should this be directly in HostBase?
    • how to find out if referencing is available?
    • do we need to know that?
Source code in client/ayon_core/host/interfaces.py
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
class ILoadHost:
    """Implementation requirements to be able use reference of representations.

    The load plugins can do referencing even without implementation of methods
    here, but switch and removement of containers would not be possible.

    Questions:
        - Is list container dependency of host or load plugins?
        - Should this be directly in HostBase?
            - how to find out if referencing is available?
            - do we need to know that?
    """

    @staticmethod
    def get_missing_load_methods(host):
        """Look for missing methods on "old type" host implementation.

        Method is used for validation of implemented functions related to
        loading. Checks only existence of methods.

        Args:
            Union[ModuleType, HostBase]: Object of host where to look for
                required methods.

        Returns:
            list[str]: Missing method implementations for loading workflow.
        """

        if isinstance(host, ILoadHost):
            return []

        required = ["ls"]
        missing = []
        for name in required:
            if not hasattr(host, name):
                missing.append(name)
        return missing

    @staticmethod
    def validate_load_methods(host):
        """Validate implemented methods of "old type" host for load workflow.

        Args:
            Union[ModuleType, HostBase]: Object of host to validate.

        Raises:
            MissingMethodsError: If there are missing methods on host
                implementation.
        """
        missing = ILoadHost.get_missing_load_methods(host)
        if missing:
            raise MissingMethodsError(host, missing)

    @abstractmethod
    def get_containers(self):
        """Retrieve referenced containers from scene.

        This can be implemented in hosts where referencing can be used.

        Todo:
            Rename function to something more self explanatory.
                Suggestion: 'get_containers'

        Returns:
            list[dict]: Information about loaded containers.
        """

        pass

    # --- Deprecated method names ---
    def ls(self):
        """Deprecated variant of 'get_containers'.

        Todo:
            Remove when all usages are replaced.
        """

        return self.get_containers()

get_containers() abstractmethod

Retrieve referenced containers from scene.

This can be implemented in hosts where referencing can be used.

Todo

Rename function to something more self explanatory. Suggestion: 'get_containers'

Returns:

Type Description

list[dict]: Information about loaded containers.

Source code in client/ayon_core/host/interfaces.py
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
@abstractmethod
def get_containers(self):
    """Retrieve referenced containers from scene.

    This can be implemented in hosts where referencing can be used.

    Todo:
        Rename function to something more self explanatory.
            Suggestion: 'get_containers'

    Returns:
        list[dict]: Information about loaded containers.
    """

    pass

get_missing_load_methods(host) staticmethod

Look for missing methods on "old type" host implementation.

Method is used for validation of implemented functions related to loading. Checks only existence of methods.

Parameters:

Name Type Description Default
Union[ModuleType, HostBase]

Object of host where to look for required methods.

required

Returns:

Type Description

list[str]: Missing method implementations for loading workflow.

Source code in client/ayon_core/host/interfaces.py
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
@staticmethod
def get_missing_load_methods(host):
    """Look for missing methods on "old type" host implementation.

    Method is used for validation of implemented functions related to
    loading. Checks only existence of methods.

    Args:
        Union[ModuleType, HostBase]: Object of host where to look for
            required methods.

    Returns:
        list[str]: Missing method implementations for loading workflow.
    """

    if isinstance(host, ILoadHost):
        return []

    required = ["ls"]
    missing = []
    for name in required:
        if not hasattr(host, name):
            missing.append(name)
    return missing

ls()

Deprecated variant of 'get_containers'.

Todo

Remove when all usages are replaced.

Source code in client/ayon_core/host/interfaces.py
 98
 99
100
101
102
103
104
105
def ls(self):
    """Deprecated variant of 'get_containers'.

    Todo:
        Remove when all usages are replaced.
    """

    return self.get_containers()

validate_load_methods(host) staticmethod

Validate implemented methods of "old type" host for load workflow.

Parameters:

Name Type Description Default
Union[ModuleType, HostBase]

Object of host to validate.

required

Raises:

Type Description
MissingMethodsError

If there are missing methods on host implementation.

Source code in client/ayon_core/host/interfaces.py
66
67
68
69
70
71
72
73
74
75
76
77
78
79
@staticmethod
def validate_load_methods(host):
    """Validate implemented methods of "old type" host for load workflow.

    Args:
        Union[ModuleType, HostBase]: Object of host to validate.

    Raises:
        MissingMethodsError: If there are missing methods on host
            implementation.
    """
    missing = ILoadHost.get_missing_load_methods(host)
    if missing:
        raise MissingMethodsError(host, missing)

INewPublisher

Bases: IPublishHost

Legacy interface replaced by 'IPublishHost'.

Deprecated

'INewPublisher' is replaced by 'IPublishHost' please change your imports. There is no "reasonable" way hot mark these classes as deprecated to show warning of wrong import. Deprecated since 3.14. will be removed in 3.15.

Source code in client/ayon_core/host/interfaces.py
373
374
375
376
377
378
379
380
381
382
383
384
class INewPublisher(IPublishHost):
    """Legacy interface replaced by 'IPublishHost'.

    Deprecated:
        'INewPublisher' is replaced by 'IPublishHost' please change your
        imports.
        There is no "reasonable" way hot mark these classes as deprecated
        to show warning of wrong import. Deprecated since 3.14.* will be
        removed in 3.15.*
    """

    pass

IPublishHost

Functions related to new creation system in new publisher.

New publisher is not storing information only about each created instance but also some global data. At this moment are data related only to context publish plugins but that can extend in future.

Source code in client/ayon_core/host/interfaces.py
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
class IPublishHost:
    """Functions related to new creation system in new publisher.

    New publisher is not storing information only about each created instance
    but also some global data. At this moment are data related only to context
    publish plugins but that can extend in future.
    """

    @staticmethod
    def get_missing_publish_methods(host):
        """Look for missing methods on "old type" host implementation.

        Method is used for validation of implemented functions related to
        new publish creation. Checks only existence of methods.

        Args:
            Union[ModuleType, HostBase]: Host module where to look for
                required methods.

        Returns:
            list[str]: Missing method implementations for new publisher
                workflow.
        """

        if isinstance(host, IPublishHost):
            return []

        required = [
            "get_context_data",
            "update_context_data",
            "get_context_title",
            "get_current_context",
        ]
        missing = []
        for name in required:
            if not hasattr(host, name):
                missing.append(name)
        return missing

    @staticmethod
    def validate_publish_methods(host):
        """Validate implemented methods of "old type" host.

        Args:
            Union[ModuleType, HostBase]: Host module to validate.

        Raises:
            MissingMethodsError: If there are missing methods on host
                implementation.
        """
        missing = IPublishHost.get_missing_publish_methods(host)
        if missing:
            raise MissingMethodsError(host, missing)

    @abstractmethod
    def get_context_data(self):
        """Get global data related to creation-publishing from workfile.

        These data are not related to any created instance but to whole
        publishing context. Not saving/returning them will cause that each
        reset of publishing resets all values to default ones.

        Context data can contain information about enabled/disabled publish
        plugins or other values that can be filled by artist.

        Returns:
            dict: Context data stored using 'update_context_data'.
        """

        pass

    @abstractmethod
    def update_context_data(self, data, changes):
        """Store global context data to workfile.

        Called when some values in context data has changed.

        Without storing the values in a way that 'get_context_data' would
        return them will each reset of publishing cause loose of filled values
        by artist. Best practice is to store values into workfile, if possible.

        Args:
            data (dict): New data as are.
            changes (dict): Only data that has been changed. Each value has
                tuple with '(<old>, <new>)' value.
        """

        pass

get_context_data() abstractmethod

Get global data related to creation-publishing from workfile.

These data are not related to any created instance but to whole publishing context. Not saving/returning them will cause that each reset of publishing resets all values to default ones.

Context data can contain information about enabled/disabled publish plugins or other values that can be filled by artist.

Returns:

Name Type Description
dict

Context data stored using 'update_context_data'.

Source code in client/ayon_core/host/interfaces.py
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
@abstractmethod
def get_context_data(self):
    """Get global data related to creation-publishing from workfile.

    These data are not related to any created instance but to whole
    publishing context. Not saving/returning them will cause that each
    reset of publishing resets all values to default ones.

    Context data can contain information about enabled/disabled publish
    plugins or other values that can be filled by artist.

    Returns:
        dict: Context data stored using 'update_context_data'.
    """

    pass

get_missing_publish_methods(host) staticmethod

Look for missing methods on "old type" host implementation.

Method is used for validation of implemented functions related to new publish creation. Checks only existence of methods.

Parameters:

Name Type Description Default
Union[ModuleType, HostBase]

Host module where to look for required methods.

required

Returns:

Type Description

list[str]: Missing method implementations for new publisher workflow.

Source code in client/ayon_core/host/interfaces.py
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
@staticmethod
def get_missing_publish_methods(host):
    """Look for missing methods on "old type" host implementation.

    Method is used for validation of implemented functions related to
    new publish creation. Checks only existence of methods.

    Args:
        Union[ModuleType, HostBase]: Host module where to look for
            required methods.

    Returns:
        list[str]: Missing method implementations for new publisher
            workflow.
    """

    if isinstance(host, IPublishHost):
        return []

    required = [
        "get_context_data",
        "update_context_data",
        "get_context_title",
        "get_current_context",
    ]
    missing = []
    for name in required:
        if not hasattr(host, name):
            missing.append(name)
    return missing

update_context_data(data, changes) abstractmethod

Store global context data to workfile.

Called when some values in context data has changed.

Without storing the values in a way that 'get_context_data' would return them will each reset of publishing cause loose of filled values by artist. Best practice is to store values into workfile, if possible.

Parameters:

Name Type Description Default
data dict

New data as are.

required
changes dict

Only data that has been changed. Each value has tuple with '(, )' value.

required
Source code in client/ayon_core/host/interfaces.py
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
@abstractmethod
def update_context_data(self, data, changes):
    """Store global context data to workfile.

    Called when some values in context data has changed.

    Without storing the values in a way that 'get_context_data' would
    return them will each reset of publishing cause loose of filled values
    by artist. Best practice is to store values into workfile, if possible.

    Args:
        data (dict): New data as are.
        changes (dict): Only data that has been changed. Each value has
            tuple with '(<old>, <new>)' value.
    """

    pass

validate_publish_methods(host) staticmethod

Validate implemented methods of "old type" host.

Parameters:

Name Type Description Default
Union[ModuleType, HostBase]

Host module to validate.

required

Raises:

Type Description
MissingMethodsError

If there are missing methods on host implementation.

Source code in client/ayon_core/host/interfaces.py
322
323
324
325
326
327
328
329
330
331
332
333
334
335
@staticmethod
def validate_publish_methods(host):
    """Validate implemented methods of "old type" host.

    Args:
        Union[ModuleType, HostBase]: Host module to validate.

    Raises:
        MissingMethodsError: If there are missing methods on host
            implementation.
    """
    missing = IPublishHost.get_missing_publish_methods(host)
    if missing:
        raise MissingMethodsError(host, missing)

IWorkfileHost

Bases: ABC

Implementation requirements to be able use workfile utils and tool.

Source code in client/ayon_core/host/interfaces.py
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
class IWorkfileHost(ABC):
    """Implementation requirements to be able use workfile utils and tool."""

    @staticmethod
    def get_missing_workfile_methods(host):
        """Look for missing methods on "old type" host implementation.

        Method is used for validation of implemented functions related to
        workfiles. Checks only existence of methods.

        Args:
            Union[ModuleType, HostBase]: Object of host where to look for
                required methods.

        Returns:
            list[str]: Missing method implementations for workfiles workflow.
        """

        if isinstance(host, IWorkfileHost):
            return []

        required = [
            "open_file",
            "save_file",
            "current_file",
            "has_unsaved_changes",
            "file_extensions",
            "work_root",
        ]
        missing = []
        for name in required:
            if not hasattr(host, name):
                missing.append(name)
        return missing

    @staticmethod
    def validate_workfile_methods(host):
        """Validate methods of "old type" host for workfiles workflow.

        Args:
            Union[ModuleType, HostBase]: Object of host to validate.

        Raises:
            MissingMethodsError: If there are missing methods on host
                implementation.
        """

        missing = IWorkfileHost.get_missing_workfile_methods(host)
        if missing:
            raise MissingMethodsError(host, missing)

    @abstractmethod
    def get_workfile_extensions(self):
        """Extensions that can be used as save.

        Questions:
            This could potentially use 'HostDefinition'.
        """

        return []

    @abstractmethod
    def save_workfile(self, dst_path=None):
        """Save currently opened scene.

        Args:
            dst_path (str): Where the current scene should be saved. Or use
                current path if 'None' is passed.
        """

        pass

    @abstractmethod
    def open_workfile(self, filepath):
        """Open passed filepath in the host.

        Args:
            filepath (str): Path to workfile.
        """

        pass

    @abstractmethod
    def get_current_workfile(self):
        """Retrieve path to current opened file.

        Returns:
            str: Path to file which is currently opened.
            None: If nothing is opened.
        """

        return None

    def workfile_has_unsaved_changes(self):
        """Currently opened scene is saved.

        Not all hosts can know if current scene is saved because the API of
        DCC does not support it.

        Returns:
            bool: True if scene is saved and False if has unsaved
                modifications.
            None: Can't tell if workfiles has modifications.
        """

        return None

    def work_root(self, session):
        """Modify workdir per host.

        Default implementation keeps workdir untouched.

        Warnings:
            We must handle this modification with more sophisticated way
            because this can't be called out of DCC so opening of last workfile
            (calculated before DCC is launched) is complicated. Also breaking
            defined work template is not a good idea.
            Only place where it's really used and can make sense is Maya. There
            workspace.mel can modify subfolders where to look for maya files.

        Args:
            session (dict): Session context data.

        Returns:
            str: Path to new workdir.
        """

        return session["AYON_WORKDIR"]

    # --- Deprecated method names ---
    def file_extensions(self):
        """Deprecated variant of 'get_workfile_extensions'.

        Todo:
            Remove when all usages are replaced.
        """
        return self.get_workfile_extensions()

    def save_file(self, dst_path=None):
        """Deprecated variant of 'save_workfile'.

        Todo:
            Remove when all usages are replaced.
        """

        self.save_workfile(dst_path)

    def open_file(self, filepath):
        """Deprecated variant of 'open_workfile'.

        Todo:
            Remove when all usages are replaced.
        """

        return self.open_workfile(filepath)

    def current_file(self):
        """Deprecated variant of 'get_current_workfile'.

        Todo:
            Remove when all usages are replaced.
        """

        return self.get_current_workfile()

    def has_unsaved_changes(self):
        """Deprecated variant of 'workfile_has_unsaved_changes'.

        Todo:
            Remove when all usages are replaced.
        """

        return self.workfile_has_unsaved_changes()

current_file()

Deprecated variant of 'get_current_workfile'.

Todo

Remove when all usages are replaced.

Source code in client/ayon_core/host/interfaces.py
264
265
266
267
268
269
270
271
def current_file(self):
    """Deprecated variant of 'get_current_workfile'.

    Todo:
        Remove when all usages are replaced.
    """

    return self.get_current_workfile()

file_extensions()

Deprecated variant of 'get_workfile_extensions'.

Todo

Remove when all usages are replaced.

Source code in client/ayon_core/host/interfaces.py
238
239
240
241
242
243
244
def file_extensions(self):
    """Deprecated variant of 'get_workfile_extensions'.

    Todo:
        Remove when all usages are replaced.
    """
    return self.get_workfile_extensions()

get_current_workfile() abstractmethod

Retrieve path to current opened file.

Returns:

Name Type Description
str

Path to file which is currently opened.

None

If nothing is opened.

Source code in client/ayon_core/host/interfaces.py
190
191
192
193
194
195
196
197
198
199
@abstractmethod
def get_current_workfile(self):
    """Retrieve path to current opened file.

    Returns:
        str: Path to file which is currently opened.
        None: If nothing is opened.
    """

    return None

get_missing_workfile_methods(host) staticmethod

Look for missing methods on "old type" host implementation.

Method is used for validation of implemented functions related to workfiles. Checks only existence of methods.

Parameters:

Name Type Description Default
Union[ModuleType, HostBase]

Object of host where to look for required methods.

required

Returns:

Type Description

list[str]: Missing method implementations for workfiles workflow.

Source code in client/ayon_core/host/interfaces.py
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
@staticmethod
def get_missing_workfile_methods(host):
    """Look for missing methods on "old type" host implementation.

    Method is used for validation of implemented functions related to
    workfiles. Checks only existence of methods.

    Args:
        Union[ModuleType, HostBase]: Object of host where to look for
            required methods.

    Returns:
        list[str]: Missing method implementations for workfiles workflow.
    """

    if isinstance(host, IWorkfileHost):
        return []

    required = [
        "open_file",
        "save_file",
        "current_file",
        "has_unsaved_changes",
        "file_extensions",
        "work_root",
    ]
    missing = []
    for name in required:
        if not hasattr(host, name):
            missing.append(name)
    return missing

get_workfile_extensions() abstractmethod

Extensions that can be used as save.

Questions

This could potentially use 'HostDefinition'.

Source code in client/ayon_core/host/interfaces.py
159
160
161
162
163
164
165
166
167
@abstractmethod
def get_workfile_extensions(self):
    """Extensions that can be used as save.

    Questions:
        This could potentially use 'HostDefinition'.
    """

    return []

has_unsaved_changes()

Deprecated variant of 'workfile_has_unsaved_changes'.

Todo

Remove when all usages are replaced.

Source code in client/ayon_core/host/interfaces.py
273
274
275
276
277
278
279
280
def has_unsaved_changes(self):
    """Deprecated variant of 'workfile_has_unsaved_changes'.

    Todo:
        Remove when all usages are replaced.
    """

    return self.workfile_has_unsaved_changes()

open_file(filepath)

Deprecated variant of 'open_workfile'.

Todo

Remove when all usages are replaced.

Source code in client/ayon_core/host/interfaces.py
255
256
257
258
259
260
261
262
def open_file(self, filepath):
    """Deprecated variant of 'open_workfile'.

    Todo:
        Remove when all usages are replaced.
    """

    return self.open_workfile(filepath)

open_workfile(filepath) abstractmethod

Open passed filepath in the host.

Parameters:

Name Type Description Default
filepath str

Path to workfile.

required
Source code in client/ayon_core/host/interfaces.py
180
181
182
183
184
185
186
187
188
@abstractmethod
def open_workfile(self, filepath):
    """Open passed filepath in the host.

    Args:
        filepath (str): Path to workfile.
    """

    pass

save_file(dst_path=None)

Deprecated variant of 'save_workfile'.

Todo

Remove when all usages are replaced.

Source code in client/ayon_core/host/interfaces.py
246
247
248
249
250
251
252
253
def save_file(self, dst_path=None):
    """Deprecated variant of 'save_workfile'.

    Todo:
        Remove when all usages are replaced.
    """

    self.save_workfile(dst_path)

save_workfile(dst_path=None) abstractmethod

Save currently opened scene.

Parameters:

Name Type Description Default
dst_path str

Where the current scene should be saved. Or use current path if 'None' is passed.

None
Source code in client/ayon_core/host/interfaces.py
169
170
171
172
173
174
175
176
177
178
@abstractmethod
def save_workfile(self, dst_path=None):
    """Save currently opened scene.

    Args:
        dst_path (str): Where the current scene should be saved. Or use
            current path if 'None' is passed.
    """

    pass

validate_workfile_methods(host) staticmethod

Validate methods of "old type" host for workfiles workflow.

Parameters:

Name Type Description Default
Union[ModuleType, HostBase]

Object of host to validate.

required

Raises:

Type Description
MissingMethodsError

If there are missing methods on host implementation.

Source code in client/ayon_core/host/interfaces.py
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
@staticmethod
def validate_workfile_methods(host):
    """Validate methods of "old type" host for workfiles workflow.

    Args:
        Union[ModuleType, HostBase]: Object of host to validate.

    Raises:
        MissingMethodsError: If there are missing methods on host
            implementation.
    """

    missing = IWorkfileHost.get_missing_workfile_methods(host)
    if missing:
        raise MissingMethodsError(host, missing)

work_root(session)

Modify workdir per host.

Default implementation keeps workdir untouched.

Parameters:

Name Type Description Default
session dict

Session context data.

required

Returns:

Name Type Description
str

Path to new workdir.

Source code in client/ayon_core/host/interfaces.py
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
def work_root(self, session):
    """Modify workdir per host.

    Default implementation keeps workdir untouched.

    Warnings:
        We must handle this modification with more sophisticated way
        because this can't be called out of DCC so opening of last workfile
        (calculated before DCC is launched) is complicated. Also breaking
        defined work template is not a good idea.
        Only place where it's really used and can make sense is Maya. There
        workspace.mel can modify subfolders where to look for maya files.

    Args:
        session (dict): Session context data.

    Returns:
        str: Path to new workdir.
    """

    return session["AYON_WORKDIR"]

workfile_has_unsaved_changes()

Currently opened scene is saved.

Not all hosts can know if current scene is saved because the API of DCC does not support it.

Returns:

Name Type Description
bool

True if scene is saved and False if has unsaved modifications.

None

Can't tell if workfiles has modifications.

Source code in client/ayon_core/host/interfaces.py
201
202
203
204
205
206
207
208
209
210
211
212
213
def workfile_has_unsaved_changes(self):
    """Currently opened scene is saved.

    Not all hosts can know if current scene is saved because the API of
    DCC does not support it.

    Returns:
        bool: True if scene is saved and False if has unsaved
            modifications.
        None: Can't tell if workfiles has modifications.
    """

    return None