Skip to content

dirmap

Dirmap functionality used in host integrations inside DCCs.

Idea for current dirmap implementation was used from Maya where is possible to enter source and destination roots and maya will try each found source in referenced file replace with each destination paths. First path which exists is used.

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