diff --git a/pype/__init__.py b/pype/__init__.py index 505db4c57f..9ca0380bf3 100644 --- a/pype/__init__.py +++ b/pype/__init__.py @@ -3,7 +3,7 @@ import os from pyblish import api as pyblish from avalon import api as avalon from .lib import filter_pyblish_plugins -from pypeapp import config, Roots +from pypeapp import config, Anatomy import logging @@ -100,8 +100,9 @@ def install(): avalon.register_plugin_path(avalon.InventoryAction, path) if project_name: - root_obj = Roots(project_name) - avalon.register_root(root_obj.roots) + anatomy = Anatomy(project_name) + anatomy.set_root_environments() + avalon.register_root(anatomy.roots) # apply monkey patched discover to original one avalon.discover = patched_discover diff --git a/pype/avalon_apps/avalon_app.py b/pype/avalon_apps/avalon_app.py index 35ab4c1eb7..d3190a9d53 100644 --- a/pype/avalon_apps/avalon_app.py +++ b/pype/avalon_apps/avalon_app.py @@ -45,10 +45,9 @@ class AvalonApps: def show_launcher(self): # if app_launcher don't exist create it/otherwise only show main window if self.app_launcher is None: - root = os.path.realpath(os.environ["AVALON_PROJECTS"]) io.install() APP_PATH = launcher_lib.resource("qml", "main.qml") - self.app_launcher = launcher_widget.Launcher(root, APP_PATH) + self.app_launcher = launcher_widget.Launcher(APP_PATH) self.app_launcher.window.show() def show_library_loader(self): diff --git a/pype/ftrack/actions/action_delete_old_versions.py b/pype/ftrack/actions/action_delete_old_versions.py index c13845f58c..30f786e93f 100644 --- a/pype/ftrack/actions/action_delete_old_versions.py +++ b/pype/ftrack/actions/action_delete_old_versions.py @@ -42,36 +42,8 @@ class DeleteOldVersions(BaseAction): return False def interface(self, session, entities, event): + # TODO Add roots existence validation items = [] - root = os.environ.get("AVALON_PROJECTS") - if not root: - msg = "Root path to projects is not set." - items.append({ - "type": "label", - "value": "ERROR: {}".format(msg) - }) - self.show_interface( - items=items, title=self.inteface_title, event=event - ) - return { - "success": False, - "message": msg - } - - if not os.path.exists(root): - msg = "Root path does not exists \"{}\".".format(str(root)) - items.append({ - "type": "label", - "value": "ERROR: {}".format(msg) - }) - self.show_interface( - items=items, title=self.inteface_title, event=event - ) - return { - "success": False, - "message": msg - } - values = event["data"].get("values") if values: versions_count = int(values["last_versions_count"]) diff --git a/pype/ftrack/actions/action_delivery.py b/pype/ftrack/actions/action_delivery.py index 9d686929de..23da81d383 100644 --- a/pype/ftrack/actions/action_delivery.py +++ b/pype/ftrack/actions/action_delivery.py @@ -340,7 +340,7 @@ class Delivery(BaseAction): repre_path = self.path_from_represenation(repre, anatomy) # TODO add backup solution where root of path from component - # is repalced with AVALON_PROJECTS root + # is repalced with root if not frame: self.process_single_file( repre_path, anatomy, anatomy_name, anatomy_data diff --git a/pype/nuke/lib.py b/pype/nuke/lib.py index 163fcd27b7..a706753755 100644 --- a/pype/nuke/lib.py +++ b/pype/nuke/lib.py @@ -177,9 +177,16 @@ def format_anatomy(data): log.debug("__ anatomy.templates: {}".format(anatomy.templates)) try: - padding = int(anatomy.templates['render']['padding']) + # TODO: bck compatibility with old anatomy template + padding = int( + anatomy.templates["render"].get( + "frame_padding", + anatomy.templates["render"].get("padding") + ) + ) except KeyError as e: msg = ("`padding` key is not in `render` " + "or `frame_padding` on is not available in " "Anatomy template. Please, add it there and restart " "the pipeline (padding: \"4\"): `{}`").format(e) @@ -973,7 +980,9 @@ class WorkfileSettings(object): self.set_colorspace() def set_favorites(self): - projects_root = os.getenv("AVALON_PROJECTS") + anatomy = get_anatomy() + work_template = anatomy.templates["work"]["path"] + projects_root = anatomy.root_value_for_template(work_template) work_dir = os.getenv("AVALON_WORKDIR") asset = os.getenv("AVALON_ASSET") project = os.getenv("AVALON_PROJECT") diff --git a/pype/nukestudio/lib.py b/pype/nukestudio/lib.py index 774a9d45bf..3a8e35f100 100644 --- a/pype/nukestudio/lib.py +++ b/pype/nukestudio/lib.py @@ -6,7 +6,7 @@ import pyblish.api import avalon.api as avalon from avalon.vendor.Qt import (QtWidgets, QtGui) import pype.api as pype -from pypeapp import Logger +from pypeapp import Logger, Anatomy log = Logger().get_logger(__name__, "nukestudio") @@ -30,12 +30,17 @@ def set_workfiles(): # show workfile gui workfiles.show(workdir) + def sync_avalon_data_to_workfile(): # import session to get project dir - S = avalon.Session - active_project_root = os.path.normpath( - os.path.join(S['AVALON_PROJECTS'], S['AVALON_PROJECT']) - ) + project_name = avalon.Session["AVALON_PROJECT"] + + anatomy = Anatomy(project_name) + work_template = anatomy.templates["work"]["path"] + work_root = anatomy.root_value_for_template(work_template) + active_project_root = ( + os.path.join(work_root, project_name) + ).replace("\\", "/") # getting project project = hiero.core.projects()[-1] @@ -350,17 +355,19 @@ def CreateNukeWorkfile(nodes=None, # create root node and save all metadata root_node = hiero.core.nuke.RootNode() - root_path = os.environ["AVALON_PROJECTS"] + anatomy = Anatomy(os.environ["AVALON_PROJECT"]) + work_template = anatomy.templates["work"]["path"] + root_path = anatomy.root_value_for_template(work_template) nuke_script.addNode(root_node) # here to call pype.nuke.lib.BuildWorkfile script_builder = nklib.BuildWorkfile( - root_node=root_node, - root_path=root_path, - nodes=nuke_script.getNodes(), - **kwargs - ) + root_node=root_node, + root_path=root_path, + nodes=nuke_script.getNodes(), + **kwargs + ) class ClipLoader: diff --git a/pype/plugins/adobecommunicator/publish/collect_context.py b/pype/plugins/adobecommunicator/publish/collect_context.py index 139dd86480..6d05825844 100644 --- a/pype/plugins/adobecommunicator/publish/collect_context.py +++ b/pype/plugins/adobecommunicator/publish/collect_context.py @@ -39,10 +39,8 @@ class CollectContextDataFromAport(pyblish.api.ContextPlugin): # get avalon session data and convert \ to / _S = avalon.session - projects = Path(_S["AVALON_PROJECTS"]).resolve() asset = _S["AVALON_ASSET"] workdir = Path(_S["AVALON_WORKDIR"]).resolve() - _S["AVALON_PROJECTS"] = str(projects) _S["AVALON_WORKDIR"] = str(workdir) context.data["avalonSession"] = _S diff --git a/pype/plugins/global/publish/integrate_master_version.py b/pype/plugins/global/publish/integrate_master_version.py index e6e4247dd8..d82c3be075 100644 --- a/pype/plugins/global/publish/integrate_master_version.py +++ b/pype/plugins/global/publish/integrate_master_version.py @@ -356,8 +356,11 @@ class IntegrateMasterVersion(pyblish.api.InstancePlugin): _anatomy_filled = anatomy.format(anatomy_data) _template_filled = _anatomy_filled["master"]["path"] head, tail = _template_filled.split(frame_splitter) - padding = ( - anatomy.templates["render"]["padding"] + padding = int( + anatomy.templates["render"].get( + "frame_padding", + anatomy.templates["render"].get("padding") + ) ) dst_col = clique.Collection( diff --git a/pype/plugins/global/publish/integrate_new.py b/pype/plugins/global/publish/integrate_new.py index f7ea4eebf9..08c390d040 100644 --- a/pype/plugins/global/publish/integrate_new.py +++ b/pype/plugins/global/publish/integrate_new.py @@ -341,8 +341,11 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): index_frame_start = None if repre.get("frameStart"): - frame_start_padding = ( - anatomy.templates["render"]["padding"] + frame_start_padding = int( + anatomy.templates["render"].get( + "frame_padding", + anatomy.templates["render"].get("padding") + ) ) index_frame_start = int(repre.get("frameStart")) @@ -651,7 +654,7 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): source = context.data["currentFile"] anatomy = instance.context.data["anatomy"] success, rootless_path = ( - anatomy.roots_obj.find_root_template_from_path(source) + anatomy.find_root_template_from_path(source) ) if success: source = rootless_path diff --git a/pype/plugins/global/publish/submit_publish_job.py b/pype/plugins/global/publish/submit_publish_job.py index f6ad9f76af..2f4e0a5ae1 100644 --- a/pype/plugins/global/publish/submit_publish_job.py +++ b/pype/plugins/global/publish/submit_publish_job.py @@ -195,7 +195,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): output_dir = instance.data["outputDir"] # Convert output dir to `{root}/rest/of/path/...` with Anatomy success, rootless_path = ( - self.anatomy.roots_obj.find_root_template_from_path(output_dir) + self.anatomy.find_root_template_from_path(output_dir) ) if not success: # `rootless_path` is not set to `output_dir` if none of roots match @@ -379,7 +379,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): staging = os.path.dirname(list(cols[0])[0]) success, rootless_staging_dir = ( - self.anatomy.roots_obj.find_root_template_from_path(staging) + self.anatomy.find_root_template_from_path(staging) ) if success: staging = rootless_staging_dir @@ -471,7 +471,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): staging = os.path.dirname(list(collection)[0]) success, rootless_staging_dir = ( - self.anatomy.roots_obj.find_root_template_from_path(staging) + self.anatomy.find_root_template_from_path(staging) ) if success: staging = rootless_staging_dir @@ -506,7 +506,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): staging = os.path.dirname(remainder) success, rootless_staging_dir = ( - self.anatomy.roots_obj.find_root_template_from_path(staging) + self.anatomy.find_root_template_from_path(staging) ) if success: staging = rootless_staging_dir @@ -619,7 +619,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): source = context.data["currentFile"] success, rootless_path = ( - self.anatomy.roots_obj.find_root_template_from_path(source) + self.anatomy.find_root_template_from_path(source) ) if success: source = rootless_path @@ -684,7 +684,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): staging_dir = repre.get("stagingDir") if staging_dir: success, rootless_staging_dir = ( - self.anatomy.roots_obj.find_root_template_from_path( + self.anatomy.find_root_template_from_path( staging_dir ) ) diff --git a/pype/plugins/maya/publish/validate_ass_relative_paths.py b/pype/plugins/maya/publish/validate_ass_relative_paths.py index b0fd12a550..b64e23e92c 100644 --- a/pype/plugins/maya/publish/validate_ass_relative_paths.py +++ b/pype/plugins/maya/publish/validate_ass_relative_paths.py @@ -37,50 +37,71 @@ class ValidateAssRelativePaths(pyblish.api.InstancePlugin): scene_dir, scene_basename = os.path.split(cmds.file(q=True, loc=True)) scene_name, _ = os.path.splitext(scene_basename) - project_root = "{}{}{}".format( - os.environ.get("AVALON_PROJECTS"), - os.path.sep, - os.environ.get("AVALON_PROJECT") - ) assert self.maya_is_true(relative_texture) is not True, \ ("Texture path is set to be absolute") assert self.maya_is_true(relative_procedural) is not True, \ ("Procedural path is set to be absolute") - texture_search_path = texture_search_path.replace("\\", "/") - procedural_search_path = procedural_search_path.replace("\\", "/") - project_root = project_root.replace("\\", "/") + anatomy = instance.context.data["anatomy"] - assert project_root in texture_search_path, \ - ("Project root is not in texture_search_path") - assert project_root in procedural_search_path, \ - ("Project root is not in procedural_search_path") + # Use project root variables for multiplatform support, see: + # https://docs.arnoldrenderer.com/display/A5AFMUG/Search+Path + # ':' as path separator is supported by Arnold for all platforms. + keys = anatomy.root_environments().keys() + paths = [] + for k in keys: + paths.append("[{}]".format(k)) + + self.log.info("discovered roots: {}".format(":".join(paths))) + + assert ":".join(paths) in texture_search_path, ( + "Project roots are not in texture_search_path" + ) + + assert ":".join(paths) in procedural_search_path, ( + "Project roots are not in procedural_search_path" + ) @classmethod def repair(cls, instance): - texture_search_path = cmds.getAttr( - "defaultArnoldRenderOptions.tspath" + texture_path = cmds.getAttr("defaultArnoldRenderOptions.tspath") + procedural_path = cmds.getAttr("defaultArnoldRenderOptions.pspath") + + # Use project root variables for multiplatform support, see: + # https://docs.arnoldrenderer.com/display/A5AFMUG/Search+Path + # ':' as path separator is supported by Arnold for all platforms. + anatomy = instance.context.data["anatomy"] + keys = anatomy.root_environments().keys() + paths = [] + for k in keys: + paths.append("[{}]".format(k)) + + cmds.setAttr( + "defaultArnoldRenderOptions.tspath", + ":".join([p for p in paths + [texture_path] if p]), + type="string" ) - procedural_search_path = cmds.getAttr( - "defaultArnoldRenderOptions.pspath" + cmds.setAttr( + "defaultArnoldRenderOptions.absolute_texture_paths", + False ) - project_root = "{}{}{}".format( - os.environ.get("AVALON_PROJECTS"), - os.path.sep, - os.environ.get("AVALON_PROJECT"), - ).replace("\\", "/") + cmds.setAttr( + "defaultArnoldRenderOptions.pspath", + ":".join([p for p in paths + [procedural_path] if p]), + type="string" + ) + cmds.setAttr( + "defaultArnoldRenderOptions.absolute_procedural_paths", + False + ) - cmds.setAttr("defaultArnoldRenderOptions.tspath", - project_root + os.pathsep + texture_search_path, - type="string") - cmds.setAttr("defaultArnoldRenderOptions.pspath", - project_root + os.pathsep + procedural_search_path, - type="string") - cmds.setAttr("defaultArnoldRenderOptions.absolute_procedural_paths", - False) - cmds.setAttr("defaultArnoldRenderOptions.absolute_texture_paths", - False) + @staticmethod + def find_absolute_path(relative_path, all_root_paths): + for root_path in all_root_paths: + possible_path = os.path.join(root_path, relative_path) + if os.path.exists(possible_path): + return possible_path def maya_is_true(self, attr_val): """ diff --git a/pype/plugins/nukestudio/publish/collect_plates.py b/pype/plugins/nukestudio/publish/collect_plates.py index 8a79354bbf..3e5ba51b60 100644 --- a/pype/plugins/nukestudio/publish/collect_plates.py +++ b/pype/plugins/nukestudio/publish/collect_plates.py @@ -104,7 +104,12 @@ class CollectPlatesData(api.InstancePlugin): version_data = dict() context = instance.context anatomy = context.data.get("anatomy", None) - padding = int(anatomy.templates['render']['padding']) + padding = int( + anatomy.templates["render"].get( + "frame_padding", + anatomy.templates["render"].get("padding") + ) + ) name = instance.data["subset"] source_path = instance.data["sourcePath"] diff --git a/pype/plugins/nukestudio/publish/collect_project_root.py b/pype/plugins/nukestudio/publish/collect_project_root.py deleted file mode 100644 index 1b21a6b641..0000000000 --- a/pype/plugins/nukestudio/publish/collect_project_root.py +++ /dev/null @@ -1,15 +0,0 @@ -import pyblish.api -import avalon.api as avalon -import os - -class CollectActiveProjectRoot(pyblish.api.ContextPlugin): - """Inject the active project into context""" - - label = "Collect Project Root" - order = pyblish.api.CollectorOrder - 0.1 - - def process(self, context): - S = avalon.Session - context.data["projectroot"] = os.path.normpath( - os.path.join(S['AVALON_PROJECTS'], S['AVALON_PROJECT']) - ) diff --git a/schema/session-2.0.json b/schema/session-2.0.json index 006a9e2dbf..d37f2ac822 100644 --- a/schema/session-2.0.json +++ b/schema/session-2.0.json @@ -9,7 +9,6 @@ "additionalProperties": true, "required": [ - "AVALON_PROJECTS", "AVALON_PROJECT", "AVALON_ASSET", "AVALON_CONFIG"