mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 08:24:53 +01:00
Merge pull request #3971 from pypeclub/feature/OP-4180_Anatomy-not-using-settings-functions
V4 - added new parent BaseAnatomy class
This commit is contained in:
commit
29bd963c97
1 changed files with 180 additions and 35 deletions
|
|
@ -6,8 +6,17 @@ import collections
|
||||||
import numbers
|
import numbers
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
import time
|
||||||
|
|
||||||
from openpype.settings.lib import get_anatomy_settings
|
from openpype.settings.lib import (
|
||||||
|
get_project_settings,
|
||||||
|
get_local_settings,
|
||||||
|
)
|
||||||
|
from openpype.settings.constants import (
|
||||||
|
DEFAULT_PROJECT_KEY
|
||||||
|
)
|
||||||
|
|
||||||
|
from openpype.client import get_project
|
||||||
from openpype.lib.path_templates import (
|
from openpype.lib.path_templates import (
|
||||||
TemplateUnsolved,
|
TemplateUnsolved,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
|
|
@ -39,34 +48,23 @@ class RootCombinationError(Exception):
|
||||||
super(RootCombinationError, self).__init__(msg)
|
super(RootCombinationError, self).__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
class Anatomy:
|
class BaseAnatomy(object):
|
||||||
"""Anatomy module helps to keep project settings.
|
"""Anatomy module helps to keep project settings.
|
||||||
|
|
||||||
Wraps key project specifications, AnatomyTemplates and Roots.
|
Wraps key project specifications, AnatomyTemplates and Roots.
|
||||||
|
|
||||||
Args:
|
|
||||||
project_name (str): Project name to look on overrides.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
root_key_regex = re.compile(r"{(root?[^}]+)}")
|
root_key_regex = re.compile(r"{(root?[^}]+)}")
|
||||||
root_name_regex = re.compile(r"root\[([^]]+)\]")
|
root_name_regex = re.compile(r"root\[([^]]+)\]")
|
||||||
|
|
||||||
def __init__(self, project_name=None, site_name=None):
|
def __init__(self, project_doc, local_settings, site_name):
|
||||||
if not project_name:
|
project_name = project_doc["name"]
|
||||||
project_name = os.environ.get("AVALON_PROJECT")
|
|
||||||
|
|
||||||
if not project_name:
|
|
||||||
raise ProjectNotSet((
|
|
||||||
"Implementation bug: Project name is not set. Anatomy requires"
|
|
||||||
" to load data for specific project."
|
|
||||||
))
|
|
||||||
|
|
||||||
self.project_name = project_name
|
self.project_name = project_name
|
||||||
|
|
||||||
self._data = self._prepare_anatomy_data(
|
|
||||||
get_anatomy_settings(project_name, site_name)
|
|
||||||
)
|
|
||||||
self._site_name = site_name
|
self._site_name = site_name
|
||||||
|
|
||||||
|
self._data = self._prepare_anatomy_data(
|
||||||
|
project_doc, local_settings, site_name
|
||||||
|
)
|
||||||
self._templates_obj = AnatomyTemplates(self)
|
self._templates_obj = AnatomyTemplates(self)
|
||||||
self._roots_obj = Roots(self)
|
self._roots_obj = Roots(self)
|
||||||
|
|
||||||
|
|
@ -87,12 +85,14 @@ class Anatomy:
|
||||||
def items(self):
|
def items(self):
|
||||||
return copy.deepcopy(self._data).items()
|
return copy.deepcopy(self._data).items()
|
||||||
|
|
||||||
@staticmethod
|
def _prepare_anatomy_data(self, project_doc, local_settings, site_name):
|
||||||
def _prepare_anatomy_data(anatomy_data):
|
|
||||||
"""Prepare anatomy data for further processing.
|
"""Prepare anatomy data for further processing.
|
||||||
|
|
||||||
Method added to replace `{task}` with `{task[name]}` in templates.
|
Method added to replace `{task}` with `{task[name]}` in templates.
|
||||||
"""
|
"""
|
||||||
|
project_name = project_doc["name"]
|
||||||
|
anatomy_data = self._project_doc_to_anatomy_data(project_doc)
|
||||||
|
|
||||||
templates_data = anatomy_data.get("templates")
|
templates_data = anatomy_data.get("templates")
|
||||||
if templates_data:
|
if templates_data:
|
||||||
# Replace `{task}` with `{task[name]}` in templates
|
# Replace `{task}` with `{task[name]}` in templates
|
||||||
|
|
@ -103,23 +103,13 @@ class Anatomy:
|
||||||
if not isinstance(item, dict):
|
if not isinstance(item, dict):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for key in tuple(item.keys()):
|
self._apply_local_settings_on_anatomy_data(anatomy_data,
|
||||||
value = item[key]
|
local_settings,
|
||||||
if isinstance(value, dict):
|
project_name,
|
||||||
value_queue.append(value)
|
site_name)
|
||||||
|
|
||||||
elif isinstance(value, six.string_types):
|
|
||||||
item[key] = value.replace("{task}", "{task[name]}")
|
|
||||||
return anatomy_data
|
return anatomy_data
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
"""Reset values of cached data in templates and roots objects."""
|
|
||||||
self._data = self._prepare_anatomy_data(
|
|
||||||
get_anatomy_settings(self.project_name, self._site_name)
|
|
||||||
)
|
|
||||||
self.templates_obj.reset()
|
|
||||||
self.roots_obj.reset()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def templates(self):
|
def templates(self):
|
||||||
"""Wrap property `templates` of Anatomy's AnatomyTemplates instance."""
|
"""Wrap property `templates` of Anatomy's AnatomyTemplates instance."""
|
||||||
|
|
@ -338,6 +328,161 @@ class Anatomy:
|
||||||
data = self.root_environmets_fill_data(template)
|
data = self.root_environmets_fill_data(template)
|
||||||
return rootless_path.format(**data)
|
return rootless_path.format(**data)
|
||||||
|
|
||||||
|
def _project_doc_to_anatomy_data(self, project_doc):
|
||||||
|
"""Convert project document to anatomy data.
|
||||||
|
|
||||||
|
Probably should fill missing keys and values.
|
||||||
|
"""
|
||||||
|
|
||||||
|
output = copy.deepcopy(project_doc["config"])
|
||||||
|
output["attributes"] = copy.deepcopy(project_doc["data"])
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
def _apply_local_settings_on_anatomy_data(
|
||||||
|
self, anatomy_data, local_settings, project_name, site_name
|
||||||
|
):
|
||||||
|
"""Apply local settings on anatomy data.
|
||||||
|
|
||||||
|
ATM local settings can modify project roots. Project name is required
|
||||||
|
as local settings have data stored data by project's name.
|
||||||
|
|
||||||
|
Local settings override root values in this order:
|
||||||
|
1.) Check if local settings contain overrides for default project and
|
||||||
|
apply it's values on roots if there are any.
|
||||||
|
2.) If passed `project_name` is not None then check project specific
|
||||||
|
overrides in local settings for the project and apply it's value on
|
||||||
|
roots if there are any.
|
||||||
|
|
||||||
|
NOTE: Root values of default project from local settings are always
|
||||||
|
applied if are set.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
anatomy_data (dict): Data for anatomy.
|
||||||
|
local_settings (dict): Data of local settings.
|
||||||
|
project_name (str): Name of project for which anatomy data are.
|
||||||
|
"""
|
||||||
|
if not local_settings:
|
||||||
|
return
|
||||||
|
|
||||||
|
local_project_settings = local_settings.get("projects") or {}
|
||||||
|
|
||||||
|
# Check for roots existence in local settings first
|
||||||
|
roots_project_locals = (
|
||||||
|
local_project_settings
|
||||||
|
.get(project_name, {})
|
||||||
|
)
|
||||||
|
roots_default_locals = (
|
||||||
|
local_project_settings
|
||||||
|
.get(DEFAULT_PROJECT_KEY, {})
|
||||||
|
)
|
||||||
|
|
||||||
|
# Skip rest of processing if roots are not set
|
||||||
|
if not roots_project_locals and not roots_default_locals:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Combine roots from local settings
|
||||||
|
roots_locals = roots_default_locals.get(site_name) or {}
|
||||||
|
roots_locals.update(roots_project_locals.get(site_name) or {})
|
||||||
|
# Skip processing if roots for current active site are not available in
|
||||||
|
# local settings
|
||||||
|
if not roots_locals:
|
||||||
|
return
|
||||||
|
|
||||||
|
current_platform = platform.system().lower()
|
||||||
|
|
||||||
|
root_data = anatomy_data["roots"]
|
||||||
|
for root_name, path in roots_locals.items():
|
||||||
|
if root_name not in root_data:
|
||||||
|
continue
|
||||||
|
anatomy_data["roots"][root_name][current_platform] = (
|
||||||
|
path
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Anatomy(BaseAnatomy):
|
||||||
|
_project_cache = {}
|
||||||
|
_site_cache = {}
|
||||||
|
|
||||||
|
def __init__(self, project_name=None, site_name=None):
|
||||||
|
if not project_name:
|
||||||
|
project_name = os.environ.get("AVALON_PROJECT")
|
||||||
|
|
||||||
|
if not project_name:
|
||||||
|
raise ProjectNotSet((
|
||||||
|
"Implementation bug: Project name is not set. Anatomy requires"
|
||||||
|
" to load data for specific project."
|
||||||
|
))
|
||||||
|
|
||||||
|
project_doc = self.get_project_doc_from_cache(project_name)
|
||||||
|
local_settings = get_local_settings()
|
||||||
|
if not site_name:
|
||||||
|
site_name = self.get_site_name_from_cache(
|
||||||
|
project_name, local_settings
|
||||||
|
)
|
||||||
|
|
||||||
|
super(Anatomy, self).__init__(
|
||||||
|
project_doc,
|
||||||
|
local_settings,
|
||||||
|
site_name
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_project_doc_from_cache(cls, project_name):
|
||||||
|
project_cache = cls._project_cache.get(project_name)
|
||||||
|
if project_cache is not None:
|
||||||
|
if time.time() - project_cache["start"] > 10:
|
||||||
|
cls._project_cache.pop(project_name)
|
||||||
|
project_cache = None
|
||||||
|
|
||||||
|
if project_cache is None:
|
||||||
|
project_cache = {
|
||||||
|
"project_doc": get_project(project_name),
|
||||||
|
"start": time.time()
|
||||||
|
}
|
||||||
|
cls._project_cache[project_name] = project_cache
|
||||||
|
|
||||||
|
return copy.deepcopy(
|
||||||
|
cls._project_cache[project_name]["project_doc"]
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_site_name_from_cache(cls, project_name, local_settings):
|
||||||
|
site_cache = cls._site_cache.get(project_name)
|
||||||
|
if site_cache is not None:
|
||||||
|
if time.time() - site_cache["start"] > 10:
|
||||||
|
cls._site_cache.pop(project_name)
|
||||||
|
site_cache = None
|
||||||
|
|
||||||
|
if site_cache:
|
||||||
|
return site_cache["site_name"]
|
||||||
|
|
||||||
|
local_project_settings = local_settings.get("projects")
|
||||||
|
if not local_project_settings:
|
||||||
|
return
|
||||||
|
|
||||||
|
project_locals = local_project_settings.get(project_name) or {}
|
||||||
|
default_locals = local_project_settings.get(DEFAULT_PROJECT_KEY) or {}
|
||||||
|
active_site = (
|
||||||
|
project_locals.get("active_site")
|
||||||
|
or default_locals.get("active_site")
|
||||||
|
)
|
||||||
|
if not active_site:
|
||||||
|
project_settings = get_project_settings(project_name)
|
||||||
|
active_site = (
|
||||||
|
project_settings
|
||||||
|
["global"]
|
||||||
|
["sync_server"]
|
||||||
|
["config"]
|
||||||
|
["active_site"]
|
||||||
|
)
|
||||||
|
|
||||||
|
cls._site_cache[project_name] = {
|
||||||
|
"site_name": active_site,
|
||||||
|
"start": time.time()
|
||||||
|
}
|
||||||
|
return active_site
|
||||||
|
|
||||||
|
|
||||||
class AnatomyTemplateUnsolved(TemplateUnsolved):
|
class AnatomyTemplateUnsolved(TemplateUnsolved):
|
||||||
"""Exception for unsolved template when strict is set to True."""
|
"""Exception for unsolved template when strict is set to True."""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue