Skip to content

anatomy

Anatomy

Bases: BaseAnatomy

Source code in client/ayon_core/pipeline/anatomy/anatomy.py
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
class Anatomy(BaseAnatomy):
    _project_cache = NestedCacheItem(lifetime=10)
    _sitesync_addon_cache = CacheItem(lifetime=60)
    _default_site_id_cache = NestedCacheItem(lifetime=60)
    _root_overrides_cache = NestedCacheItem(2, lifetime=60)

    def __init__(
        self, project_name=None, site_name=None, project_entity=None
    ):
        if not project_name:
            project_name = os.environ.get("AYON_PROJECT_NAME")

        if not project_name:
            raise ProjectNotSet((
                "Implementation bug: Project name is not set. Anatomy requires"
                " to load data for specific project."
            ))

        if not project_entity:
            project_entity = self.get_project_entity_from_cache(project_name)
        root_overrides = self._get_site_root_overrides(
            project_name, site_name
        )

        super(Anatomy, self).__init__(project_entity, root_overrides)

    @classmethod
    def get_project_entity_from_cache(cls, project_name):
        project_cache = cls._project_cache[project_name]
        if not project_cache.is_valid:
            project_cache.update_data(ayon_api.get_project(project_name))
        return copy.deepcopy(project_cache.get_data())

    @classmethod
    def get_sitesync_addon(cls):
        if not cls._sitesync_addon_cache.is_valid:
            manager = AddonsManager()
            cls._sitesync_addon_cache.update_data(
                manager.get_enabled_addon("sitesync")
            )
        return cls._sitesync_addon_cache.get_data()

    @classmethod
    def _get_studio_roots_overrides(cls, project_name):
        """This would return 'studio' site override by local settings.

        Notes:
            This logic handles local overrides of studio site which may be
                available even when sync server is not enabled.
            Handling of 'studio' and 'local' site was separated as preparation
                for AYON development where that will be received from
                separated sources.

        Args:
            project_name (str): Name of project.

        Returns:
            Union[Dict[str, str], None]): Local root overrides.
        """
        if not project_name:
            return None
        return ayon_api.get_project_root_overrides_by_site_id(
            project_name, get_local_site_id()
        )

    @classmethod
    def _get_site_root_overrides(cls, project_name, site_name):
        """Get root overrides for site.

        Args:
            project_name (str): Project name for which root overrides should be
                received.
            site_name (Union[str, None]): Name of site for which root overrides
                should be returned.
        """

        # First check if sync server is available and enabled
        sitesync_addon = cls.get_sitesync_addon()
        if sitesync_addon is None or not sitesync_addon.enabled:
            # QUESTION is ok to force 'studio' when site sync is not enabled?
            site_name = "studio"

        elif not site_name:
            # Use sync server to receive active site name
            project_cache = cls._default_site_id_cache[project_name]
            if not project_cache.is_valid:
                project_cache.update_data(
                    sitesync_addon.get_active_site_type(project_name)
                )
            site_name = project_cache.get_data()

        site_cache = cls._root_overrides_cache[project_name][site_name]
        if not site_cache.is_valid:
            if site_name == "studio":
                # Handle studio root overrides without sync server
                # - studio root overrides can be done even without sync server
                roots_overrides = cls._get_studio_roots_overrides(
                    project_name
                )
            else:
                # Ask sync server to get roots overrides
                roots_overrides = sitesync_addon.get_site_root_overrides(
                    project_name, site_name
                )
            site_cache.update_data(roots_overrides)
        return site_cache.get_data()

AnatomyStringTemplate

Bases: StringTemplate

String template which has access to anatomy.

Parameters:

Name Type Description Default
anatomy_templates AnatomyTemplates

Anatomy templates object.

required
template str

Template string.

required
Source code in client/ayon_core/pipeline/anatomy/templates.py
 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
class AnatomyStringTemplate(StringTemplate):
    """String template which has access to anatomy.

    Args:
        anatomy_templates (AnatomyTemplates): Anatomy templates object.
        template (str): Template string.
    """

    def __init__(self, anatomy_templates, template):
        self.anatomy_templates = anatomy_templates
        super(AnatomyStringTemplate, self).__init__(template)

    def format(self, data):
        """Format template and add 'root' key to data if not available.

        Args:
            data (dict[str, Any]): Formatting data for template.

        Returns:
            AnatomyTemplateResult: Formatting result.
        """

        anatomy_templates = self.anatomy_templates
        if not data.get("root"):
            data = copy.deepcopy(data)
            data["root"] = anatomy_templates.anatomy.roots
        result = StringTemplate.format(self, data)
        rootless_path = anatomy_templates.get_rootless_path_from_result(
            result
        )
        return AnatomyTemplateResult(result, rootless_path)

format(data)

Format template and add 'root' key to data if not available.

Parameters:

Name Type Description Default
data dict[str, Any]

Formatting data for template.

required

Returns:

Name Type Description
AnatomyTemplateResult

Formatting result.

Source code in client/ayon_core/pipeline/anatomy/templates.py
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
def format(self, data):
    """Format template and add 'root' key to data if not available.

    Args:
        data (dict[str, Any]): Formatting data for template.

    Returns:
        AnatomyTemplateResult: Formatting result.
    """

    anatomy_templates = self.anatomy_templates
    if not data.get("root"):
        data = copy.deepcopy(data)
        data["root"] = anatomy_templates.anatomy.roots
    result = StringTemplate.format(self, data)
    rootless_path = anatomy_templates.get_rootless_path_from_result(
        result
    )
    return AnatomyTemplateResult(result, rootless_path)

AnatomyTemplateResult

Bases: TemplateResult

Source code in client/ayon_core/pipeline/anatomy/templates.py
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
class AnatomyTemplateResult(TemplateResult):
    rootless = None

    def __new__(cls, result, rootless_path):
        new_obj = super(AnatomyTemplateResult, cls).__new__(
            cls,
            str(result),
            result.template,
            result.solved,
            result.used_values,
            result.missing_keys,
            result.invalid_types
        )
        new_obj.rootless = rootless_path
        return new_obj

    def validate(self):
        if not self.solved:
            raise AnatomyTemplateUnsolved(
                self.template,
                self.missing_keys,
                self.invalid_types
            )

    def copy(self):
        tmp = TemplateResult(
            str(self),
            self.template,
            self.solved,
            self.used_values,
            self.missing_keys,
            self.invalid_types
        )
        return self.__class__(tmp, self.rootless)

    def normalized(self):
        """Convert to normalized path."""

        tmp = TemplateResult(
            os.path.normpath(self),
            self.template,
            self.solved,
            self.used_values,
            self.missing_keys,
            self.invalid_types
        )
        return self.__class__(tmp, self.rootless)

normalized()

Convert to normalized path.

Source code in client/ayon_core/pipeline/anatomy/templates.py
58
59
60
61
62
63
64
65
66
67
68
69
def normalized(self):
    """Convert to normalized path."""

    tmp = TemplateResult(
        os.path.normpath(self),
        self.template,
        self.solved,
        self.used_values,
        self.missing_keys,
        self.invalid_types
    )
    return self.__class__(tmp, self.rootless)

AnatomyTemplateUnsolved

Bases: TemplateUnsolved

Exception for unsolved template when strict is set to True.

Source code in client/ayon_core/pipeline/anatomy/exceptions.py
41
42
43
44
class AnatomyTemplateUnsolved(TemplateUnsolved):
    """Exception for unsolved template when strict is set to True."""

    msg = "Anatomy template \"{0}\" is unsolved.{1}{2}"

ProjectNotSet

Bases: Exception

Exception raised when is created Anatomy without project name.

Source code in client/ayon_core/pipeline/anatomy/exceptions.py
4
5
class ProjectNotSet(Exception):
    """Exception raised when is created Anatomy without project name."""

RootCombinationError

Bases: Exception

This exception is raised when templates has combined root types.

Source code in client/ayon_core/pipeline/anatomy/exceptions.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class RootCombinationError(Exception):
    """This exception is raised when templates has combined root types."""

    def __init__(self, roots):
        joined_roots = ", ".join(
            ["\"{}\"".format(_root) for _root in roots]
        )
        # TODO better error message
        msg = (
            "Combination of root with and"
            " without root name in AnatomyTemplates. {}"
        ).format(joined_roots)

        super(RootCombinationError, self).__init__(msg)

RootMissingEnv

Bases: KeyError

Raised when root requires environment variables which is not filled.

Source code in client/ayon_core/pipeline/anatomy/exceptions.py
 8
 9
10
class RootMissingEnv(KeyError):
    """Raised when root requires environment variables which is not filled."""
    pass

TemplateMissingKey

Bases: Exception

Exception for cases when key does not exist in template.

Source code in client/ayon_core/pipeline/anatomy/exceptions.py
29
30
31
32
33
34
35
36
37
38
class TemplateMissingKey(Exception):
    """Exception for cases when key does not exist in template."""

    msg = "Template key '{}' was not found."

    def __init__(self, parents):
        parent_join = "".join(["[\"{0}\"]".format(key) for key in parents])
        super(TemplateMissingKey, self).__init__(
            self.msg.format(parent_join)
        )