diff --git a/pype/modules/deadline/plugins/publish/submit_publish_job.py b/pype/modules/deadline/plugins/publish/submit_publish_job.py index 6a532b7635..95ed6bac10 100644 --- a/pype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/pype/modules/deadline/plugins/publish/submit_publish_job.py @@ -100,7 +100,6 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): icon = "tractor" deadline_plugin = "Pype" - hosts = ["fusion", "maya", "nuke", "celaction", "aftereffects", "harmony"] families = ["render.farm", "prerender", @@ -150,7 +149,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): # list of family names to transfer to new family if present families_transfer = ["render3d", "render2d", "ftrack", "slate"] - plugin_python_version = "3.7" + plugin_pype_version = "3.0" # script path for publish_filesequence.py publishing_script = None @@ -186,7 +185,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): ).format(output_dir)) roothless_mtdt_p = metadata_path - return (metadata_path, roothless_mtdt_p) + return metadata_path, roothless_mtdt_p def _submit_deadline_post_job(self, instance, job, instances): """Submit publish job to Deadline. @@ -220,7 +219,6 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): self._create_metadata_path(instance) environment = job["Props"].get("Env", {}) - environment["PYPE_METADATA_FILE"] = roothless_metadata_path environment["AVALON_PROJECT"] = io.Session["AVALON_PROJECT"] environment["AVALON_ASSET"] = io.Session["AVALON_ASSET"] environment["AVALON_TASK"] = io.Session["AVALON_TASK"] @@ -230,6 +228,11 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): environment["PYPE_PUBLISH_JOB"] = "1" environment["PYPE_RENDER_JOB"] = "0" + args = [ + 'publish', + roothless_metadata_path + ] + # Generate the payload for Deadline submission payload = { "JobInfo": { @@ -250,7 +253,8 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "OutputDirectory0": output_dir }, "PluginInfo": { - "Version": self.plugin_python_version, + "Version": self.plugin_pype_version, + "Arguments": " ".join(args), "SingleFrameOnly": "True", }, # Mandatory for Deadline, may be empty @@ -1069,6 +1073,6 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): self.log.info("interpreter::{}".format(interpreter)) executable_path = os.path.join(*interpreter) - assert executable_path is not None, ("Cannot determine path") + assert executable_path is not None, "Cannot determine path" return str(executable_path) diff --git a/vendor/deadline/custom/plugins/Pype/Pype.param b/vendor/deadline/custom/plugins/Pype/Pype.param index 1452163bdd..b55fd9433e 100644 --- a/vendor/deadline/custom/plugins/Pype/Pype.param +++ b/vendor/deadline/custom/plugins/Pype/Pype.param @@ -1,37 +1,27 @@ -[State] -Type=Enum -Items=Global Enabled;Opt-In;Disabled -Category=Options -CategoryOrder=0 -CategoryIndex=0 -Label=State -Default=Global Enabled -Description=How this event plug-in should respond to events. If Global, all jobs and slaves will trigger the events for this plugin. If Opt-In, jobs and slaves can choose to trigger the events for this plugin. If Disabled, no events are triggered for this plugin. +[About] +Type=label +Label=About +Category=About Plugin +CategoryOrder=-1 +Index=0 +Default=Pype Plugin for Deadline +Description=Not configurable -[PythonSearchPaths] -Type=MultiLineMultiFolder -Label=Additional Python Search Paths -Category=Options +[ConcurrentTasks] +Type=label +Label=ConcurrentTasks +Category=About Plugin +CategoryOrder=-1 +Index=0 +Default=True +Description=Not configurable + +[Pype_Executable_3_0] +Type=multilinemultifilename +Label=Pype 3.0 Executable +Category=Pype Executables CategoryOrder=0 -CategoryIndex=1 +Index=0 Default= -Description=The list of paths to append to the PYTHONPATH environment variable. This allows the Python job to find custom modules in non-standard locations. +Description=The path to the Pype executable. Enter alternative paths on separate lines. -[LoggingLevel] -Type=Enum -Label=Logging Level -Category=Options -CategoryOrder=0 -CategoryIndex=2 -Items=DEBUG;INFO;WARNING;ERROR -Default=DEBUG -Description=Logging level where printing will start. - -[PypeExecutable] -Type=MultiLineMultiFolder -Label=Path to Pype executable dir -Category=Job Plugins -CategoryOrder=1 -CategoryIndex=1 -Default= -Description= \ No newline at end of file diff --git a/vendor/deadline/custom/plugins/Pype/Pype.py b/vendor/deadline/custom/plugins/Pype/Pype.py index ec4e958bc1..af25b38b63 100644 --- a/vendor/deadline/custom/plugins/Pype/Pype.py +++ b/vendor/deadline/custom/plugins/Pype/Pype.py @@ -1,9 +1,10 @@ -import sys -import subprocess -import platform -import os +from System.IO import * +from System.Text.RegularExpressions import * -from Deadline.Plugins import DeadlinePlugin, PluginType +from Deadline.Plugins import * +from Deadline.Scripting import * + +import re ###################################################################### @@ -28,84 +29,88 @@ class PypeDeadlinePlugin(DeadlinePlugin): for publish process. """ def __init__(self): - self.StartJobCallback += self.StartJob self.InitializeProcessCallback += self.InitializeProcess - self.RenderTasksCallback += self.RenderTasks + self.RenderExecutableCallback += self.RenderExecutable + self.RenderArgumentCallback += self.RenderArgument def Cleanup(self): - del self.StartJobCallback + for stdoutHandler in self.StdoutHandlers: + del stdoutHandler.HandleCallback + del self.InitializeProcessCallback - del self.RenderTasksCallback + del self.RenderExecutableCallback + del self.RenderArgumentCallback - def StartJob(self): - # adding python search paths - paths = self.GetConfigEntryWithDefault("PythonSearchPaths", "").strip() - paths = paths.split(";") - - for path in paths: - self.LogInfo("Extending sys.path with: " + str(path)) - sys.path.append(path) - - self.LogInfo("PypeDeadlinePlugin start") - try: - metadata_file = \ - self.GetProcessEnvironmentVariable("PYPE_METADATA_FILE") - if not metadata_file: - raise RuntimeError("Env var PYPE_METADATA_FILE value missing") - - pype_app = self.get_pype_executable_path() - - args = [ - pype_app, - 'publish', - metadata_file - ] - - env = {} - env = dict(os.environ) - - job = self.GetJob() - for key in job.GetJobEnvironmentKeys(): - env[str(key)] = str(job.GetJobEnvironmentKeyValue(key)) - - exit_code = subprocess.call(args, shell=True, env=env) - if exit_code != 0: - raise RuntimeError("Publishing failed, check worker's log") - - self.LogInfo("PypeDeadlinePlugin end") - except Exception: - import traceback - self.LogInfo(traceback.format_exc()) - self.LogInfo("PypeDeadlinePlugin failed") - raise - - # Called by Deadline to initialize the process. def InitializeProcess(self): - # Set the plugin specific settings. self.PluginType = PluginType.Simple + self.StdoutHandling = True - def RenderTasks(self): - # do nothing, no render, just publishing, still must be here - pass + self.SingleFramesOnly = self.GetBooleanPluginInfoEntryWithDefault( + "SingleFramesOnly", False) + self.LogInfo("Single Frames Only: %s" % self.SingleFramesOnly) - def get_pype_executable_path(self): - """ - Returns calculated path based on settings and platform + self.AddStdoutHandlerCallback( + ".*Progress: (\d+)%.*").HandleCallback += self.HandleProgress - Uses 'pype_console' executable - """ - pype_command = "pype_console" - if platform.system().lower() == "linux": - pype_command = "pype_console.sh" - if platform.system().lower() == "windows": - pype_command = "pype_console.exe" + def RenderExecutable(self): + version = self.GetPluginInfoEntry("Version") - pype_root = self.GetConfigEntryWithDefault("PypeExecutable", "") + exeList = self.GetConfigEntry( + "Pype_Executable_" + version.replace(".", "_")) + exe = FileUtils.SearchFileList(exeList) + if exe == "": + self.FailRender( + "Pype " + version + " executable was not found " + + "in the semicolon separated list \"" + exeList + "\". " + + "The path to the render executable can be configured " + + "from the Plugin Configuration in the Deadline Monitor.") + return exe - pype_app = os.path.join(pype_root.strip(), pype_command) - if not os.path.exists(pype_app): - raise RuntimeError("App '{}' doesn't exist. " + - "Fix it in Tools > Configure Events > " + - "pype".format(pype_app)) + def RenderArgument(self): + arguments = str(self.GetPluginInfoEntryWithDefault("Arguments", "")) + arguments = RepositoryUtils.CheckPathMapping(arguments) - return pype_app + arguments = re.sub(r"<(?i)STARTFRAME>", str(self.GetStartFrame()), + arguments) + arguments = re.sub(r"<(?i)ENDFRAME>", str(self.GetEndFrame()), + arguments) + arguments = re.sub(r"<(?i)QUOTE>", "\"", arguments) + + arguments = self.ReplacePaddedFrame(arguments, + "<(?i)STARTFRAME%([0-9]+)>", + self.GetStartFrame()) + arguments = self.ReplacePaddedFrame(arguments, + "<(?i)ENDFRAME%([0-9]+)>", + self.GetEndFrame()) + + count = 0 + for filename in self.GetAuxiliaryFilenames(): + localAuxFile = Path.Combine(self.GetJobsDataDirectory(), filename) + arguments = re.sub(r"<(?i)AUXFILE" + str(count) + r">", + localAuxFile.replace("\\", "/"), arguments) + count += 1 + + return arguments + + def ReplacePaddedFrame(self, arguments, pattern, frame): + frameRegex = Regex(pattern) + while True: + frameMatch = frameRegex.Match(arguments) + if frameMatch.Success: + paddingSize = int(frameMatch.Groups[1].Value) + if paddingSize > 0: + padding = StringUtils.ToZeroPaddedString(frame, + paddingSize, + False) + else: + padding = str(frame) + arguments = arguments.replace(frameMatch.Groups[0].Value, + padding) + else: + break + + return arguments + + def HandleProgress(self): + progress = float(self.GetRegexMatch(1)) + self.SetProgress(progress)