Skip to content

roots

AnatomyRoots

Object which should be used for formatting "root" key in templates.

Parameters:

Name Type Description Default
anatomy Anatomy

Anatomy object created for a specific project.

required
Source code in client/ayon_core/pipeline/anatomy/roots.py
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
281
282
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
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
class AnatomyRoots:
    """Object which should be used for formatting "root" key in templates.

    Args:
        anatomy (Anatomy): Anatomy object created for a specific project.
    """

    env_prefix = "AYON_PROJECT_ROOT"

    def __init__(self, anatomy):
        self._log = None
        self._anatomy = anatomy
        self._loaded_project = None
        self._roots = None

    def __format__(self, *args, **kwargs):
        return self.roots.__format__(*args, **kwargs)

    def __getitem__(self, key):
        return self.roots[key]

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

    @property
    def anatomy(self):
        """Parent Anatomy object.

        Returns:
            Anatomy: Parent anatomy object.

        """
        return self._anatomy

    def reset(self):
        """Reset current roots value."""
        self._roots = None

    def path_remapper(
        self, path, dst_platform=None, src_platform=None, roots=None
    ):
        """Remap path for specific platform.

        Args:
            path (str): Source path which need to be remapped.
            dst_platform (Optional[str]): Specify destination platform
                for which remapping should happen.
            src_platform (Optional[str]): Specify source platform. This is
                recommended to not use and keep unset until you really want
                to use specific platform.
            roots (Optional[Union[dict, RootItem])): It is possible to remap
                path with different roots then instance where method was
                called has.

        Returns:
            Union[str, None]: When path does not contain known root then
                None is returned else returns remapped path with "{root}"
                or "{root[<name>]}".

        """
        if roots is None:
            roots = self.roots

        if roots is None:
            raise ValueError("Roots are not set. Can't find path.")

        if "{root" in path:
            path = path.format(**{"root": roots})
            # If `dst_platform` is not specified then return else continue.
            if not dst_platform:
                return path

        if isinstance(roots, RootItem):
            return roots.path_remapper(path, dst_platform, src_platform)

        for _root in roots.values():
            result = self.path_remapper(
                path, dst_platform, src_platform, _root
            )
            if result is not None:
                return result

    def find_root_template_from_path(self, path, roots=None):
        """Find root value in entered path and replace it with formatting key.

        Args:
            path (str): Source path where root will be searched.
            roots (Optional[Union[AnatomyRoots, dict]): It is possible to use
                different roots than instance where method was triggered has.

        Returns:
            tuple: Output contains tuple with bool representing success as
                first value and path with or without replaced root with
                formatting key as second value.

        Raises:
            ValueError: When roots are not entered and can't be loaded.
        """
        if roots is None:
            self.log.debug(
                "Looking for matching root in path \"{}\".".format(path)
            )
            roots = self.roots

        if roots is None:
            raise ValueError("Roots are not set. Can't find path.")

        if isinstance(roots, RootItem):
            return roots.find_root_template_from_path(path)

        for root_name, _root in roots.items():
            success, result = self.find_root_template_from_path(path, _root)
            if success:
                self.log.debug(
                    "Found match in root \"{}\".".format(root_name)
                )
                return success, result

        self.log.warning("No matching root was found in current setting.")
        return (False, path)

    def set_root_environments(self):
        """Set root environments for current project."""
        for key, value in self.root_environments().items():
            os.environ[key] = value

    def root_environments(self):
        """Use root keys to create unique keys for environment variables.

        Concatenates prefix "AYON_PROJECT_ROOT_" with root keys to create
        unique keys.

        Returns:
            dict: Result is `{(str): (str)}` dicitonary where key represents
                unique key concatenated by keys and value is root value of
                current platform root.

        Example:
            With raw root values::
                "work": {
                    "windows": "P:/projects/work",
                    "linux": "/mnt/share/projects/work",
                    "darwin": "/darwin/path/work"
                },
                "publish": {
                    "windows": "P:/projects/publish",
                    "linux": "/mnt/share/projects/publish",
                    "darwin": "/darwin/path/publish"
                }

            Result on windows platform::
                {
                    "AYON_PROJECT_ROOT_WORK": "P:/projects/work",
                    "AYON_PROJECT_ROOT_PUBLISH": "P:/projects/publish"
                }

        """
        return self._root_environments()

    def all_root_paths(self, roots=None):
        """Return all paths for all roots of all platforms."""
        if roots is None:
            roots = self.roots

        output = []
        if isinstance(roots, RootItem):
            for value in roots.raw_data.values():
                output.append(value)
            return output

        for _roots in roots.values():
            output.extend(self.all_root_paths(_roots))
        return output

    def _root_environments(self, keys=None, roots=None):
        if not keys:
            keys = []
        if roots is None:
            roots = self.roots

        if isinstance(roots, RootItem):
            key_items = [self.env_prefix]
            for _key in keys:
                key_items.append(_key.upper())

            key = "_".join(key_items)
            # Make sure key and value does not contain unicode
            #   - can happen in Python 2 hosts
            return {str(key): str(roots.value)}

        output = {}
        for _key, _value in roots.items():
            _keys = list(keys)
            _keys.append(_key)
            output.update(self._root_environments(_keys, _value))
        return output

    def root_environmets_fill_data(self, template=None):
        """Environment variable values in dictionary for rootless path.

        Args:
            template (str): Template for environment variable key fill.
                By default is set to `"${}"`.
        """
        if template is None:
            template = "${}"
        return self._root_environmets_fill_data(template)

    def _root_environmets_fill_data(self, template, keys=None, roots=None):
        if keys is None and roots is None:
            return {
                "root": self._root_environmets_fill_data(
                    template, [], self.roots
                )
            }

        if isinstance(roots, RootItem):
            key_items = [AnatomyRoots.env_prefix]
            for _key in keys:
                key_items.append(_key.upper())
            key = "_".join(key_items)
            return template.format(key)

        output = {}
        for key, value in roots.items():
            _keys = list(keys)
            _keys.append(key)
            output[key] = self._root_environmets_fill_data(
                template, _keys, value
            )
        return output

    @property
    def project_name(self):
        """Current project name which will be used for loading root values.

        Returns:
            str: Project name.
        """
        return self._anatomy.project_name

    @property
    def roots(self):
        """Property for filling "root" key in templates.

        This property returns roots for current project or default root values.

        Warning:
            Default roots value may cause issues when project use different
            roots settings. That may happen when project use multiroot
            templates but default roots miss their keys.

        """
        if self.project_name != self._loaded_project:
            self._roots = None

        if self._roots is None:
            self._roots = self._discover()
            self._loaded_project = self.project_name
        return self._roots

    def _discover(self):
        """ Loads current project's roots or default.

        Default roots are loaded if project override's does not contain roots.

        Returns:
            `RootItem` or `dict` with multiple `RootItem`s when multiroot
            setting is used.
        """

        return self._parse_dict(self._anatomy["roots"], self)

    @staticmethod
    def _parse_dict(data, parent):
        """Parse roots raw data into RootItem or dictionary with RootItems.

        Converting raw roots data to `RootItem` helps to handle platform keys.
        This method is recursive to be able handle multiroot setup and
        is static to be able to load default roots without creating new object.

        Args:
            data (dict): Should contain raw roots data to be parsed.
            parent (AnatomyRoots): Parent object set as parent
                for ``RootItem``.

        Returns:
            dict[str, RootItem]: Root items by name.

        """
        output = {}
        for root_name, root_values in data.items():
            output[root_name] = RootItem(
                parent, root_values, root_name
            )
        return output

anatomy property

Parent Anatomy object.

Returns:

Name Type Description
Anatomy

Parent anatomy object.

project_name property

Current project name which will be used for loading root values.

Returns:

Name Type Description
str

Project name.

roots property

Property for filling "root" key in templates.

This property returns roots for current project or default root values.

Warning

Default roots value may cause issues when project use different roots settings. That may happen when project use multiroot templates but default roots miss their keys.

all_root_paths(roots=None)

Return all paths for all roots of all platforms.

Source code in client/ayon_core/pipeline/anatomy/roots.py
388
389
390
391
392
393
394
395
396
397
398
399
400
401
def all_root_paths(self, roots=None):
    """Return all paths for all roots of all platforms."""
    if roots is None:
        roots = self.roots

    output = []
    if isinstance(roots, RootItem):
        for value in roots.raw_data.values():
            output.append(value)
        return output

    for _roots in roots.values():
        output.extend(self.all_root_paths(_roots))
    return output

find_root_template_from_path(path, roots=None)

Find root value in entered path and replace it with formatting key.

Parameters:

Name Type Description Default
path str

Source path where root will be searched.

required
roots Optional[Union[AnatomyRoots, dict]

It is possible to use different roots than instance where method was triggered has.

None

Returns:

Name Type Description
tuple

Output contains tuple with bool representing success as first value and path with or without replaced root with formatting key as second value.

Raises:

Type Description
ValueError

When roots are not entered and can't be loaded.

Source code in client/ayon_core/pipeline/anatomy/roots.py
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
def find_root_template_from_path(self, path, roots=None):
    """Find root value in entered path and replace it with formatting key.

    Args:
        path (str): Source path where root will be searched.
        roots (Optional[Union[AnatomyRoots, dict]): It is possible to use
            different roots than instance where method was triggered has.

    Returns:
        tuple: Output contains tuple with bool representing success as
            first value and path with or without replaced root with
            formatting key as second value.

    Raises:
        ValueError: When roots are not entered and can't be loaded.
    """
    if roots is None:
        self.log.debug(
            "Looking for matching root in path \"{}\".".format(path)
        )
        roots = self.roots

    if roots is None:
        raise ValueError("Roots are not set. Can't find path.")

    if isinstance(roots, RootItem):
        return roots.find_root_template_from_path(path)

    for root_name, _root in roots.items():
        success, result = self.find_root_template_from_path(path, _root)
        if success:
            self.log.debug(
                "Found match in root \"{}\".".format(root_name)
            )
            return success, result

    self.log.warning("No matching root was found in current setting.")
    return (False, path)

path_remapper(path, dst_platform=None, src_platform=None, roots=None)

Remap path for specific platform.

Parameters:

Name Type Description Default
path str

Source path which need to be remapped.

required
dst_platform Optional[str]

Specify destination platform for which remapping should happen.

None
src_platform Optional[str]

Specify source platform. This is recommended to not use and keep unset until you really want to use specific platform.

None
roots Optional[Union[dict, RootItem]

It is possible to remap path with different roots then instance where method was called has.

None

Returns:

Type Description

Union[str, None]: When path does not contain known root then None is returned else returns remapped path with "{root}" or "{root[]}".

Source code in client/ayon_core/pipeline/anatomy/roots.py
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
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
def path_remapper(
    self, path, dst_platform=None, src_platform=None, roots=None
):
    """Remap path for specific platform.

    Args:
        path (str): Source path which need to be remapped.
        dst_platform (Optional[str]): Specify destination platform
            for which remapping should happen.
        src_platform (Optional[str]): Specify source platform. This is
            recommended to not use and keep unset until you really want
            to use specific platform.
        roots (Optional[Union[dict, RootItem])): It is possible to remap
            path with different roots then instance where method was
            called has.

    Returns:
        Union[str, None]: When path does not contain known root then
            None is returned else returns remapped path with "{root}"
            or "{root[<name>]}".

    """
    if roots is None:
        roots = self.roots

    if roots is None:
        raise ValueError("Roots are not set. Can't find path.")

    if "{root" in path:
        path = path.format(**{"root": roots})
        # If `dst_platform` is not specified then return else continue.
        if not dst_platform:
            return path

    if isinstance(roots, RootItem):
        return roots.path_remapper(path, dst_platform, src_platform)

    for _root in roots.values():
        result = self.path_remapper(
            path, dst_platform, src_platform, _root
        )
        if result is not None:
            return result

reset()

Reset current roots value.

Source code in client/ayon_core/pipeline/anatomy/roots.py
263
264
265
def reset(self):
    """Reset current roots value."""
    self._roots = None

root_environments()

Use root keys to create unique keys for environment variables.

Concatenates prefix "AYON_PROJECT_ROOT_" with root keys to create unique keys.

Returns:

Name Type Description
dict

Result is {(str): (str)} dicitonary where key represents unique key concatenated by keys and value is root value of current platform root.

Example

With raw root values:: "work": { "windows": "P:/projects/work", "linux": "/mnt/share/projects/work", "darwin": "/darwin/path/work" }, "publish": { "windows": "P:/projects/publish", "linux": "/mnt/share/projects/publish", "darwin": "/darwin/path/publish" }

Result on windows platform:: { "AYON_PROJECT_ROOT_WORK": "P:/projects/work", "AYON_PROJECT_ROOT_PUBLISH": "P:/projects/publish" }

Source code in client/ayon_core/pipeline/anatomy/roots.py
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
def root_environments(self):
    """Use root keys to create unique keys for environment variables.

    Concatenates prefix "AYON_PROJECT_ROOT_" with root keys to create
    unique keys.

    Returns:
        dict: Result is `{(str): (str)}` dicitonary where key represents
            unique key concatenated by keys and value is root value of
            current platform root.

    Example:
        With raw root values::
            "work": {
                "windows": "P:/projects/work",
                "linux": "/mnt/share/projects/work",
                "darwin": "/darwin/path/work"
            },
            "publish": {
                "windows": "P:/projects/publish",
                "linux": "/mnt/share/projects/publish",
                "darwin": "/darwin/path/publish"
            }

        Result on windows platform::
            {
                "AYON_PROJECT_ROOT_WORK": "P:/projects/work",
                "AYON_PROJECT_ROOT_PUBLISH": "P:/projects/publish"
            }

    """
    return self._root_environments()

root_environmets_fill_data(template=None)

Environment variable values in dictionary for rootless path.

Parameters:

Name Type Description Default
template str

Template for environment variable key fill. By default is set to "${}".

None
Source code in client/ayon_core/pipeline/anatomy/roots.py
426
427
428
429
430
431
432
433
434
435
def root_environmets_fill_data(self, template=None):
    """Environment variable values in dictionary for rootless path.

    Args:
        template (str): Template for environment variable key fill.
            By default is set to `"${}"`.
    """
    if template is None:
        template = "${}"
    return self._root_environmets_fill_data(template)

set_root_environments()

Set root environments for current project.

Source code in client/ayon_core/pipeline/anatomy/roots.py
350
351
352
353
def set_root_environments(self):
    """Set root environments for current project."""
    for key, value in self.root_environments().items():
        os.environ[key] = value

RootItem

Bases: FormatObject

Represents one item or roots.

Holds raw data of root item specification. Raw data contain value for each platform, but current platform value is used when object is used for formatting of template.

Parameters:

Name Type Description Default
parent AnatomyRoots

Parent object.

required
root_raw_data dict

Dictionary containing root values by platform names. ["windows", "linux" and "darwin"]

required
name str

Root name which is representing. Used with multi root setup otherwise None value is expected.

required
Source code in client/ayon_core/pipeline/anatomy/roots.py
  9
 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
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
class RootItem(FormatObject):
    """Represents one item or roots.

    Holds raw data of root item specification. Raw data contain value
    for each platform, but current platform value is used when object
    is used for formatting of template.

    Args:
        parent (AnatomyRoots): Parent object.
        root_raw_data (dict): Dictionary containing root values by platform
            names. ["windows", "linux" and "darwin"]
        name (str): Root name which is representing. Used with
            multi root setup otherwise None value is expected.
    """
    def __init__(self, parent, root_raw_data, name):
        super(RootItem, self).__init__()
        self._log = None
        lowered_platform_keys = {}
        for key, value in root_raw_data.items():
            lowered_platform_keys[key.lower()] = value
        self.raw_data = lowered_platform_keys
        self.cleaned_data = self._clean_roots(lowered_platform_keys)
        self.name = name
        self.parent = parent

        self.available_platforms = set(lowered_platform_keys.keys())
        self.value = lowered_platform_keys.get(platform.system().lower())
        self.clean_value = self._clean_root(self.value)

    def __format__(self, *args, **kwargs):
        return self.value.__format__(*args, **kwargs)

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

    def __repr__(self):
        return self.__str__()

    def __getitem__(self, key):
        if isinstance(key, numbers.Number):
            return self.value[key]

        additional_info = ""
        if self.parent and self.parent.project_name:
            additional_info += " for project \"{}\"".format(
                self.parent.project_name
            )

        raise KeyError(
            "Root key \"{}\" is missing{}.".format(
                key, additional_info
            )
        )

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

    @property
    def full_key(self):
        """Full key value for dictionary formatting in template.

        Returns:
            str: Return full replacement key for formatting. This helps when
                multiple roots are set. In that case e.g. `"root[work]"` is
                returned.

        """
        return "root[{}]".format(self.name)

    @staticmethod
    def _clean_path(path):
        """Just replace backslashes with forward slashes.

        Args:
            path (str): Path which should be cleaned.

        Returns:
            str: Cleaned path with forward slashes.

        """
        return str(path).replace("\\", "/")

    def _clean_root(self, root):
        """Clean root value.

        Args:
            root (str): Root value which should be cleaned.

        Returns:
            str: Cleaned root value.

        """
        return self._clean_path(root).rstrip("/")

    def _clean_roots(self, raw_data):
        """Clean all values of raw root item values."""
        cleaned = {}
        for key, value in raw_data.items():
            cleaned[key] = self._clean_root(value)
        return cleaned

    def path_remapper(self, path, dst_platform=None, src_platform=None):
        """Remap path for specific platform.

        Args:
            path (str): Source path which need to be remapped.
            dst_platform (str, optional): Specify destination platform
                for which remapping should happen.
            src_platform (str, optional): Specify source platform. This is
                recommended to not use and keep unset until you really want
                to use specific platform.

        Returns:
            Union[str, None]: When path does not contain known root then
                None is returned else returns remapped path with
                "{root[<name>]}".

        """
        cleaned_path = self._clean_path(path)
        if dst_platform:
            dst_root_clean = self.cleaned_data.get(dst_platform)
            if not dst_root_clean:
                self.log.warning(
                    "Root \"{}\" miss platform \"{}\" definition.".format(
                        self.full_key, dst_platform
                    )
                )
                return None

            if cleaned_path.startswith(dst_root_clean):
                return cleaned_path

        if src_platform:
            src_root_clean = self.cleaned_data.get(src_platform)
            if src_root_clean is None:
                self.log.warning(
                    "Root \"{}\" miss platform \"{}\" definition.".format(
                        self.full_key, src_platform
                    )
                )
                return None

            if not cleaned_path.startswith(src_root_clean):
                return None

            subpath = cleaned_path[len(src_root_clean):]
            if dst_platform:
                # `dst_root_clean` is used from upper condition
                return dst_root_clean + subpath
            return self.clean_value + subpath

        result, template = self.find_root_template_from_path(path)
        if not result:
            return None

        if dst_platform:
            fill_data = {self.name: dst_root_clean}
        else:
            fill_data = {self.name: self.value}

        return template.format(**{"root": fill_data})

    def find_root_template_from_path(self, path):
        """Replaces known root value with formattable key in path.

        All platform values are checked for this replacement.

        Args:
            path (str): Path where root value should be found.

        Returns:
            tuple: Tuple contain 2 values: `success` (bool) and `path` (str).
                When success it True then path should contain replaced root
                value with formattable key.

        Example:
            When input path is::
                "C:/windows/path/root/projects/my_project/file.ext"

            And raw data of item looks like::
                {
                    "windows": "C:/windows/path/root",
                    "linux": "/mount/root"
                }

            Output will be::
                (True, "{root}/projects/my_project/file.ext")

            If any of raw data value wouldn't match path's root output is::
                (False, "C:/windows/path/root/projects/my_project/file.ext")
        """
        result = False
        output = str(path)

        mod_path = self._clean_path(path)
        for root_os, root_path in self.cleaned_data.items():
            # Skip empty paths
            if not root_path:
                continue

            _mod_path = mod_path  # reset to original cleaned value
            if root_os == "windows":
                root_path = root_path.lower()
                _mod_path = _mod_path.lower()

            if _mod_path.startswith(root_path):
                result = True
                replacement = "{" + self.full_key + "}"
                output = replacement + mod_path[len(root_path):]
                break

        return (result, output)

full_key property

Full key value for dictionary formatting in template.

Returns:

Name Type Description
str

Return full replacement key for formatting. This helps when multiple roots are set. In that case e.g. "root[work]" is returned.

find_root_template_from_path(path)

Replaces known root value with formattable key in path.

All platform values are checked for this replacement.

Parameters:

Name Type Description Default
path str

Path where root value should be found.

required

Returns:

Name Type Description
tuple

Tuple contain 2 values: success (bool) and path (str). When success it True then path should contain replaced root value with formattable key.

Example

When input path is:: "C:/windows/path/root/projects/my_project/file.ext"

And raw data of item looks like:: { "windows": "C:/windows/path/root", "linux": "/mount/root" }

Output will be:: (True, "{root}/projects/my_project/file.ext")

If any of raw data value wouldn't match path's root output is:: (False, "C:/windows/path/root/projects/my_project/file.ext")

Source code in client/ayon_core/pipeline/anatomy/roots.py
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
def find_root_template_from_path(self, path):
    """Replaces known root value with formattable key in path.

    All platform values are checked for this replacement.

    Args:
        path (str): Path where root value should be found.

    Returns:
        tuple: Tuple contain 2 values: `success` (bool) and `path` (str).
            When success it True then path should contain replaced root
            value with formattable key.

    Example:
        When input path is::
            "C:/windows/path/root/projects/my_project/file.ext"

        And raw data of item looks like::
            {
                "windows": "C:/windows/path/root",
                "linux": "/mount/root"
            }

        Output will be::
            (True, "{root}/projects/my_project/file.ext")

        If any of raw data value wouldn't match path's root output is::
            (False, "C:/windows/path/root/projects/my_project/file.ext")
    """
    result = False
    output = str(path)

    mod_path = self._clean_path(path)
    for root_os, root_path in self.cleaned_data.items():
        # Skip empty paths
        if not root_path:
            continue

        _mod_path = mod_path  # reset to original cleaned value
        if root_os == "windows":
            root_path = root_path.lower()
            _mod_path = _mod_path.lower()

        if _mod_path.startswith(root_path):
            result = True
            replacement = "{" + self.full_key + "}"
            output = replacement + mod_path[len(root_path):]
            break

    return (result, output)

path_remapper(path, dst_platform=None, src_platform=None)

Remap path for specific platform.

Parameters:

Name Type Description Default
path str

Source path which need to be remapped.

required
dst_platform str

Specify destination platform for which remapping should happen.

None
src_platform str

Specify source platform. This is recommended to not use and keep unset until you really want to use specific platform.

None

Returns:

Type Description

Union[str, None]: When path does not contain known root then None is returned else returns remapped path with "{root[]}".

Source code in client/ayon_core/pipeline/anatomy/roots.py
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
def path_remapper(self, path, dst_platform=None, src_platform=None):
    """Remap path for specific platform.

    Args:
        path (str): Source path which need to be remapped.
        dst_platform (str, optional): Specify destination platform
            for which remapping should happen.
        src_platform (str, optional): Specify source platform. This is
            recommended to not use and keep unset until you really want
            to use specific platform.

    Returns:
        Union[str, None]: When path does not contain known root then
            None is returned else returns remapped path with
            "{root[<name>]}".

    """
    cleaned_path = self._clean_path(path)
    if dst_platform:
        dst_root_clean = self.cleaned_data.get(dst_platform)
        if not dst_root_clean:
            self.log.warning(
                "Root \"{}\" miss platform \"{}\" definition.".format(
                    self.full_key, dst_platform
                )
            )
            return None

        if cleaned_path.startswith(dst_root_clean):
            return cleaned_path

    if src_platform:
        src_root_clean = self.cleaned_data.get(src_platform)
        if src_root_clean is None:
            self.log.warning(
                "Root \"{}\" miss platform \"{}\" definition.".format(
                    self.full_key, src_platform
                )
            )
            return None

        if not cleaned_path.startswith(src_root_clean):
            return None

        subpath = cleaned_path[len(src_root_clean):]
        if dst_platform:
            # `dst_root_clean` is used from upper condition
            return dst_root_clean + subpath
        return self.clean_value + subpath

    result, template = self.find_root_template_from_path(path)
    if not result:
        return None

    if dst_platform:
        fill_data = {self.name: dst_root_clean}
    else:
        fill_data = {self.name: self.value}

    return template.format(**{"root": fill_data})