Application Plugins

Overview

All of Deadline’s plug-ins are written in Python, which means that it’s easy to create your own plug-ins or customize the existing ones. See the Scripting Overview documentation for more information, and links to the Deadline Scripting reference.

Note that because the Python scripts for application plug-ins will be executed in a non-interactive way, it is important that your scripts do not contain any blocking operations like infinite loops, or interfaces that require user input.

When a plugin is loaded the log will show where the plugin is being loaded from.

General Plug-in Information

There are two types of plug-ins that can be created:

  • Simple
  • Advanced

Simple plug-ins provide the basics to wrap a command line application, and is typically used to build up command line arguments to pass to the application. Advanced plug-ins provide more control, and are typically used when running a simple command line application isn’t enough. Other than the plug-in Python script itself though, Simple and Advanced plug-ins are very similar.

Creating a New Plug-in

This section covers the the areas that Simple and Advanced plug-ins have in common. Specifics for Simple and Advanced plug-ins are covered later on.

To create a new plug-in, start by creating a folder in the Repository’s custom\plugins folder and give it the name of your plug-in. See the Scripting Overview documentation for more information on the ‘custom’ folder in the Repository and how it’s used.

For the sake of this document, we will call our new plug-in MyPlugin. All relative script and configuration files for this plug-in are to be placed in this plug-in’s folder (some are required and some are optional).

The dlinit File - Required

The first required file is MyPlugin.dlinit, which is the main configuration file for this plug-in. It is a plain text file that defines a few general key=value plug-in properties, which include:

Key Name Description
About A short description of the plug-in.
ConcurrentTasks Set to True or False (default is False). If tasks for this plug-in can render concurrently without interfering with each other, this can be set to True.
DebugLogging Set to True or False (default is False). If set to True, then debug plug-in logging will be printed out during rendering.
DeprecatedMode Set to True or False (default is False). Only set to True if you want a custom Python.NET plug-in from Deadline 5.1 or 5.2 to work with Deadline 6 or later. More information on DeprecatedMode can be found later on.

It can also define key=value custom settings to be used by the plug-in. A common custom setting is the executable to use to render the job. For this example, our MyPlugin.dlinit file might look like this:

About=My Example Plugin for Deadline
# This is a comment
ConcurrentTasks=True
MyPluginRenderExecutable=c:\path\to\my\executable.exe

The py File - Required

The other required file is MyPlugin.py, which is the main plug-in script file. It defines the main DeadlinePlugin class that contains the necessary code that Deadline uses to render a job. This is where Simple and Advanced plug-ins will differ, and the specifics for each can be found later on, but the template for this script file might look like this:

from Deadline.Plugins import *

######################################################################
## This is the function that Deadline calls to get an instance of the
## main DeadlinePlugin class.
######################################################################
def GetDeadlinePlugin():
    return MyPlugin()

######################################################################
## This is the function that Deadline calls when the plugin is no
## longer in use so that it can get cleaned up.
######################################################################
def CleanupDeadlinePlugin( deadlinePlugin ):
    deadlinePlugin.Cleanup()

######################################################################
## This is the main DeadlinePlugin class for MyPlugin.
######################################################################
class MyPlugin (DeadlinePlugin):

    # TODO: Place code here instead of "pass"
    pass

The first thing to note is that we’re importing the Deadline.Plugins namespace so that we can access the DeadlinePlugin class.

The GetDeadlinePlugin() function is important, as it allows the Slave to get an instance of our MyPlugin class (which is extending the abstract DeadlinePlugin class). In Deadline 6.2 and later, the GetDeadlinePluginWithJob( job ) function can be defined as an alternative. It works the same as GetDeadlinePlugin(), except that it accepts an instance of the Job object that the plug-in is being loaded for. If either of these functions are not defined, the Slave will report an error when it tries to render the job.

The MyPlugin class will need to implement certain callbacks based on the type of plug-in it is, and these callbacks must be hooked up in the MyPlugin constructor. One callback that all plug-ins should implement is the InitializeProcess function. There are many other callbacks that can be implemented, which are covered in the Events section for the DeadlinePlugin class in the Deadline Scripting reference.

The CleanupDeadlinePlugin() function is also important, as it is necessary to clean up the plug-in when it is no longer in use. Typically, this is used to clean up any callbacks that were created when the plug-in was initialized.

To start off, the InitializeProcess callback is typically used to set some general plug-in settings:

from Deadline.Plugins import *

######################################################################
## This is the function that Deadline calls to get an instance of the
## main DeadlinePlugin class.
######################################################################
def GetDeadlinePlugin():
    return MyPlugin()

######################################################################
## This is the function that Deadline calls when the plugin is no
## longer in use so that it can get cleaned up.
######################################################################
def CleanupDeadlinePlugin( deadlinePlugin ):
    deadlinePlugin.Cleanup()

######################################################################
## This is the main DeadlinePlugin class for MyPlugin.
######################################################################
class MyPlugin (DeadlinePlugin):

    ## Hook up the callbacks in the constructor.
    def __init__( self ):
        self.InitializeProcessCallback += self.InitializeProcess

    ## Clean up the plugin.
    def Cleanup():
        del self.InitializeProcessCallback

    ## Called by Deadline to initialize the plugin.
    def InitializeProcess( self ):
        # Set the plugin specific settings.
        self.SingleFramesOnly = False
        self.PluginType = PluginType.Simple

These are the common plug-in properties that can be set in InitializeProcess callback. See the DeadlinePlugin class in the Deadline Scripting reference for additional properties.

Property Description
PluginType The type of plug-in this is (PluginType.Simple/PluginType.Advanced).
SingleFramesOnly Set to True or False. Set to True if your plug-in can only work on one frame at a time, rather than a frame sequence.

The param File - Optional

The MyPlugin.param file is an optional file that is used by the Plugin Configuration dialog in the Monitor. It declares properties that the Monitor uses to generate a user interface for modifying custom settings in the MyPlugin.dlinit file. After you’ve created this file, open the Monitor and enter Super User mode. Then select Tools -> Configure Plugins and look for your plug-in in the list on the left.

../_images/configure_plugin_myplugin.png

The file might look something like:

[MyPluginRenderExecutable]
Type=filename
Label=My Plugin Render Executable
Default=c:\path\to\my\executable.exe
Description=The path to the executable file used for rendering.

Comment lines are supported in the param file, and must start with either ‘;’ or ‘#’. For example:

# This is the file name picker control to set the executable for this plugin.
[MyPluginRenderExecutable]
Type=filename
Label=My Plugin Render Executable
Default=c:\path\to\my\executable.exe
Description=The path to the executable file used for rendering.

You’ll notice that the property name between the square brackets matches the MyPluginRenderExecutable custom setting we defined in our MyPlugin.dlinit file. This means that this control will change the MyPluginRenderExecutable setting. The available key=value pairs for the properties defined here are:

Key Name Description
Category The category the control should go under.
CategoryIndex This determines the control’s order under its category. This does the same thing as Index.
CategoryOrder This determines the category’s order among other categories. If more than one CategoryOrder is defined for the same category, the lowest value is used.
Default The default value to be used if this property is not defined in the dlinit file. This does the same thing as DefaultValue.
DefaultValue The default value to be used if this property is not defined in the dlinit file. This does the same thing as Default.
Description A short description of the property the control is for (displayed as a tooltip in the UI).
DisableIfBlank If True, a control will not be shown if this property is not defined in the dinit file (True/False). This does the same thing as IgnoreIfBlank.
IgnoreIfBlank If True, a control will not be shown if this property is not defined in the dinit file (True/False). This does the same thing as DisableIfBlank.
Index This determines the control’s order under its category. This does the same thing as CategoryIndex.
Label The control label.
Required If True, a control will be shown for this property even if it’s not defined in the dlinit file (True/False).
Type The type of control (see table below).

These are the available controls.

Control Type Description
Boolean A drop-down control that allows the selection of True or False.
Color Allows the selection of a color.
Enum A drop-down control that allows the selection of an item from a list.
Enumeration Same as Enum above.
Filename Allows the selection of an existing file.
FilenameSave Allows the selection of a new or existing file.
Float An floating point spinner control.
Folder Allows the selection of an existing folder.
Integer An integer spinner control.
Label A read-only text field.
MultiFilename Allows the selection of multiple existing files, which are then separated by semicolons in the text field.
MultiLineMultiFilename Allows the selection of multiple existing files, which are then placed on multiple lines in the text field.
MultiLineMultiFolder Allows the selection of multiple existing folders, which are then placed on multiple lines in the text field.
MultiLineString A text field with multiple lines.
Password A text field that masks the text.
SlaveList Allows the selection of existing Slaves, which are then separated by commas in the text field.
String A text field.

There are also key/value pairs for specific controls:

Key Name Description
DecimalPlaces The number of decimal places for the Float controls.
Filter The filter string for the Filename, FilenameSave, or MultiFilename controls.
Increment The value to increment the Integer or Float controls by.
Items The semicolon separated list of items for the Enum control. This does the same thing as Values.
Maximum The maximum value for the Integer or Float controls.
Minimum The minimum value for the Integer or Float controls.
Validator A regular expression for the String control that is used to ensure the value is valid.
Values The semicolon separated list of items for the Enum control. This does the same thing as Items.

The options File - Optional

The MyPlugin.options file is an optional file that is used by the Job Properties dialog in the Monitor. It declares properties that the Monitor uses to generate a user interface for modifying plug-in specific options as they appear in the plug-in info file that was submitted with the job. After you’ve created this file, you can right-click on a job in the Monitor that uses this plug-in and select Modify Properties. You should then see a MyPlugin page at the bottom of the list on the left which you can select to view these properties.

../_images/options_file_myplugin.png

Often, these plug-in specific options are used to build up the arguments to be passed to the rendering application. Let’s assume that our render executable takes a “-verbose” argument that accepts a boolean parameter, and that the plug-in info file submitted with the job contains the following:

Verbose=True

Now we would like to be able to change this value from the Job Properties dialog in the Monitor, so our MyPlugin.options file might look like this:

[Verbose]
Type=boolean
Label=Verbose Logging
Description=If verbose logging is enabled.
Required=true
DisableIfBlank=false
DefaultValue=True

You’ll notice that the property name between the square brackets matches the Verbose setting in our plug-in info file. This means that this control will change the Verbose setting. The available key=value pairs for the properties defined here are the same as those defined for the param file above. Comment lines are also supported in the options file in the same way they are supported in the param file.

The ico File - Optional

The MyPlugin.icon file is an optional 16x16 icon file that can be used to easily identify jobs that use this plug-in in the Monitor. Typically, it is the plug-in application’s logo, or something else that represents the plug-in. If a plug-in does not have an icon file, a generic icon will be shown in the jobs list in the Monitor

The JobPreLoad.py File - Optional

The JobPreLoad.py file is an optional script that will be executed by the Slave prior to loading a job that uses this plug-in. Note that in this case, the file does not share its name with the plug-in folder. This script can be used to do things like synchronize plug-ins or scripts prior to starting the render job.

The only requirement for the PreJobLoad.py script is that you define a __main__ function, which is called by the Slave when it executes the script. It must accept a single parameter, which is the current instance of the DeadlinePlugin class. Here is an example script that copies a couple files from a server to the local machine, and sets some environment variables:

from System import *
from System.IO import *

def __main__( deadlinePlugin ):
    deadlinePlugin.LogInfo( "Copying some files" )
    File.Copy( r"\\server\files\file1.ext", r"C:\local\files\file1.ext", True )
    File.Copy( r"\\server\files\file2.ext", r"C:\local\files\file2.ext", True )

    deadlinePlugin.LogInfo( "Setting EnvVar1 to True" )
    deadlinePlugin.SetProcessEnvironmentVariable( "EnvVar1", "True" )

    deadlinePlugin.LogInfo( "Setting EnvVar2 to False" )
    deadlinePlugin.SetProcessEnvironmentVariable( "EnvVar2", "False" )

The PluginPreLoad.py File - Optional

The PluginPreLoad.py file is an optional script that will be executed by the Slave prior to executing any python script for the plug-in (MyPlugin.py or JobPreLoad.py), and any pre or post job or task script for the current job. Note that in this case, the file does not share its name with the plug-in folder. This script can be used to set up the Python environment prior to running any other python script, including setting sys.path to control where additional modules will be loaded from.

The only requirement for the PluginPreLoad.py script is that you define a __main__ function, which is called by the Slave when it executes the script. It does not accept any parameters. Here is an example script that updates sys.path with custom paths:

import sys

def __main__():
    path = r"\\server\python"
    if path not in sys.path:
        sys.path.append( path )

Simple Plug-ins

A render job goes through three stages:

  • StartJob: A job enters this stage when it is first picked up by a Slave.
  • RenderTasks: A job can enter this stage many times (once for each task a Slave dequeues while it has the current job loaded).
  • EndJob: A job enters this stage when a Slave is unloading the job.

Simple plug-ins only covers the RenderTasks stage, and are pretty straight forward. They are commonly used to render with applications that support simple command line rendering (running a command line executable and waiting for it to complete). For example, After Effects has a command line renderer called aerender.exe, which can be executed by the Slave to render specific frames of an After Effects project file.

Initialization

By default, a plug-in is considered to be a Simple plug-in, but you can explicitly set this in the InitializeProcess() callback (as explained above). You can also define settings specific to the simple plug-in, as well as any popup or stdout handlers that you need. These additional settings are covered in the ManagedProcess class in the Deadline Scripting reference (note that the DeadlinePlugin class inherits from the ManagedProcess class). For example:

from Deadline.Plugins import *

from System.Diagnostics import *

######################################################################
## This is the function that Deadline calls to get an instance of the
## main DeadlinePlugin class.
######################################################################
def GetDeadlinePlugin():
    return MyPlugin()

######################################################################
## This is the function that Deadline calls when the plugin is no
## longer in use so that it can get cleaned up.
######################################################################
def CleanupDeadlinePlugin( deadlinePlugin ):
    deadlinePlugin.Cleanup()

######################################################################
## This is the main DeadlinePlugin class for MyPlugin.
######################################################################
class MyPlugin (DeadlinePlugin):

    ## Hook up the callbacks in the constructor.
    def __init__( self ):
        self.InitializeProcessCallback += self.InitializeProcess

    ## Clean up the plugin.
    def Cleanup():
        # Clean up stdout handler callbacks.
        for stdoutHandler in self.StdoutHandlers:
            del stdoutHandler.HandleCallback

        del self.InitializeProcessCallback

    ## Called by Deadline to initialize the process.
    def InitializeProcess( self ):
        # Set the plugin specific settings.
        self.SingleFramesOnly = False
        self.PluginType = PluginType.Simple

        # Set the ManagedProcess specific settings.
        self.ProcessPriority = ProcessPriorityClass.BelowNormal
        self.UseProcessTree = True

        #StdoutHandling should be enabled if required in your plugin
        self.StdoutHandling = True

        #PopupHandling should be enabled if required in your plugin
        self.PopupHandling = True

        # Set the stdout handlers.
        self.AddStdoutHandlerCallback(
            "WARNING:.*" ).HandleCallback += self.HandleStdoutWarning
        self.AddStdoutHandlerCallback(
            "ERROR:(.*)" ).HandleCallback += self.HandleStdoutError

        # Set the popup ignorers.
        self.AddPopupIgnorer( "Popup 1" )
        self.AddPopupIgnorer( "Popup 2" )

        # Set the popup handlers.
        self.AddPopupHandler( "Popup 3", "OK" )
        self.AddPopupHandler( "Popup 4", "Do not ask me this again;Continue" )

    ## Callback for when a line of stdout contains a WARNING message.
    def HandleStdoutWarning( self ):
        self.LogWarning( self.GetRegexMatch(0) )

    ## Callback for when a line of stdout contains an ERROR message.
    def HandleStdoutError( self ):
        self.FailRender( "Detected an error: " + self.GetRegexMatch(1) )

Stdout Handlers

The AddStdoutHandlerCallback() function accepts a string parameter, which is a POSIX compliant regular expression used to match against lines of stdout from the command line process. This function also returns a RegexHandlerCallback instance, which you can hook up a callback to that is called when a line of stdout is matched. This can all be done on one line, which is shown in the example above.

Examples of handler callback functions are also shown in the example above. Within these handler functions, the GetRegexMatch() function can be used to get a specific match from the regular expression. The parameter passed to GetRegexMatch() is the index for the matches that were found. 0 returns the entire matched string, and 1, 2, etc returns the matched substrings (matches that are surrounded by round brackets). If there isn’t a corresponding substring, you’ll get an error (note that 0 is always a valid index).

In HandleStdoutWarning(), 0 is the only valid index because there is no substring in round brackets in the regular expression. In HandleStdoutError(), 0 and 1 are valid. 0 will return the entire matched string, whereas 1 will return the substring in the round brackets.

For further examples, please open up any of our application plugin Python script files and inspect them. An example of comprehensive Stdout handlers can be found in the MayaBatch plugin.

  • ../plugins/MayaBatch/MayaBatch.py

Note, that Deadline’s default shipping StdoutHandlers require the Slave’s Operating System to be using ENGLISH as it’s language.

Render Executable and Arguments

The RenderExecutable() callback is used to get the path to the executable that will be used for rendering. This callback must be implemented in a Simple plug-in, or an error will occur. Continuing our example from above, we’ll use the path specified in the MyPlugin.dlinit file, and we can access it using the global GetConfigEntry() function.

Another important (but optional) callback is the RenderArgument() callback. This callback should return the arguments you want to pass to the render executable. Typically, these arguments are built from values that are pulled from the DeadlinePlugin class (like the scene file name, or the start and end frame for the task), or from the plug-in info file that was submitted with the job using the GetPluginInfoEntry() function. If this callback is not implemented, then no arguments will be passed to the executable.

After adding these callbacks, our example plug-in script now looks like this:

from Deadline.Plugins import *

from System.Diagnostics import *

######################################################################
## This is the function that Deadline calls to get an instance of the
## main DeadlinePlugin class.
######################################################################
def GetDeadlinePlugin():
    return MyPlugin()

######################################################################
## This is the function that Deadline calls when the plugin is no
## longer in use so that it can get cleaned up.
######################################################################
def CleanupDeadlinePlugin( deadlinePlugin ):
    deadlinePlugin.Cleanup()

######################################################################
## This is the main DeadlinePlugin class for MyPlugin.
######################################################################
class MyPlugin (DeadlinePlugin):

    ## Hook up the callbacks in the constructor.
    def __init__( self ):
        self.InitializeProcessCallback += self.InitializeProcess
        self.RenderExecutableCallback += self.RenderExecutable
        self.RenderArgumentCallback += self.RenderArgument

    ## Clean up the plugin.
    def Cleanup():
        # Clean up stdout handler callbacks.
        for stdoutHandler in self.StdoutHandlers:
            del stdoutHandler.HandleCallback

        del self.InitializeProcessCallback
        del self.RenderExecutableCallback
        del self.RenderArgumentCallback

    ## Called by Deadline to initialize the process.
    def InitializeProcess( self ):
        # Set the plugin specific settings.
        self.SingleFramesOnly = False
        self.PluginType = PluginType.Simple

        # Set the ManagedProcess specific settings.
        self.ProcessPriority = ProcessPriorityClass.BelowNormal
        self.UseProcessTree = True
        self.StdoutHandling = True
        self.PopupHandling = True

        # Set the stdout handlers.
        self.AddStdoutHandlerCallback(
            "WARNING:.*" ).HandleCallback += self.HandleStdoutWarning
        self.AddStdoutHandlerCallback(
            "ERROR:(.*)" ).HandleCallback += self.HandleStdoutError

        # Set the popup ignorers.
        self.AddPopupIgnorer( "Popup 1" )
        self.AddPopupIgnorer( "Popup 2" )

        # Set the popup handlers.
        self.AddPopupHandler( "Popup 3", "OK" )
        self.AddPopupHandler( "Popup 4", "Do not ask me this again;Continue" )

    ## Callback for when a line of stdout contains a WARNING message.
    def HandleStdoutWarning( self ):
        self.LogWarning( self.GetRegexMatch(0) )

    ## Callback for when a line of stdout contains an ERROR message.
    def HandleStdoutError( self ):
        self.FailRender( "Detected an error: " + self.GetRegexMatch(1) )

    ## Callback to get the executable used for rendering.
    def RenderExecutable( self ):
        return self.GetConfigEntry( "MyPluginRenderExecutable" )

    ## Callback to get the arguments that will be passed to the executable.
    def RenderArgument( self ):
        arguments = " -continueOnError"
        arguments += " -verbose " + self.GetPluginInfoEntry( "Verbose" )
        arguments += " -start " + str(self.GetStartFrame())
        arguments += " -end " + str(self.GetEndFrame())
        arguments += " -scene \"" + self.GetDataFilename() + "\""
        return arguments

There are many other callbacks that can be implemented for Simple plug-ins, which are covered in the Events section for the ManagedProcess class in the Deadline Scripting reference. The best place to find examples of Simple plug-ins is to look at some of the plug-ins that are shipped with Deadline. These range from the very basic (Blender), to the more complex (MayaCmd).

Advanced Plug-ins

To reiterate, a render job goes through three stages:

  • StartJob: A job enters this stage when it is first picked up by a Slave.
  • RenderTasks: A job can enter this stage many times (once for each task a Slave dequeues while it has the current job loaded).
  • EndJob: A job enters this stage when a Slave is unloading the job.

Advanced plug-ins are more complex, as they control all three of these job stages. They are commonly used to render with applications that support some sort of slave/server mode that Deadline can interact with. Usually, this requires the application to be started during the StartJob phase, fed commands during the RenderTasks stage(s), and finally shut down during the EndJob stage. For example, the 3ds Max plug-in starts up 3dsmax in slave mode and forces it to load our Lightning plug-in. The Lightning plug-in listens for commands from Deadline and executes them as necessary. After rendering is complete, 3ds Max is shut down.

Initialization

To indicate that your plug-in is an Advanced plug-in, you need to set the PluginType property in the InitializeProcess() callback.

from Deadline.Plugins import *

######################################################################
## This is the function that Deadline calls to get an instance of the
## main DeadlinePlugin class.
######################################################################
def GetDeadlinePlugin():
    return MyPlugin()

######################################################################
## This is the function that Deadline calls when the plugin is no
## longer in use so that it can get cleaned up.
######################################################################
def CleanupDeadlinePlugin( deadlinePlugin ):
    deadlinePlugin.Cleanup()

######################################################################
## This is the main DeadlinePlugin class for MyPlugin.
######################################################################
class MyPlugin (DeadlinePlugin):

    ## Hook up the callbacks in the constructor.
    def __init__( self ):
        self.InitializeProcessCallback += self.InitializeProcess

    ## Clean up the plugin.
    def Cleanup():
        del self.InitializeProcessCallback

    ## Called by Deadline to initialize the process.
    def InitializeProcess( self ):
        # Set the plugin specific settings.
        self.SingleFramesOnly = False
        self.PluginType = PluginType.Advanced

Render Tasks

The RenderTasks() callback is the only required callback for Advanced plug-ins. If it is not implemented, an error will occur. It contains the code to be executed for each task that a Slave renders. This could involve launching applications, communicating with already running applications, or simply running a script to automate a particular task (like backing up a group of files).

Other common callbacks for Advanced plug-ins are the StartJob() and EndJob() callbacks. The StartJob() callback can be used to start up an application, or to set some local variables that will be used in other callbacks. If the StartJob() callback is not implemented, then nothing is done during the StartJob phase. The EndJob() callback can be used to shut down a running application, or to clean up temporary files. If the EndJob() callback is not implemented, then nothing is done during the EndJob phase.

In the example below, we will be launching our application during the StartJob phase. The benefit to this is that the application can be left running during the duration of the job, which eliminates the overhead of having to launch the application for each task. To launch and monitor the application, we will be implementing a ManagedProcess class, and calling it MyPluginProcess .This ManagedProcess class will define the render executable and command line arguments for launching the process we will be monitoring. Note that we aren’t passing it any frame information, as this needs to be handled in the RenderTasks() callback when it interacts with the process.

After adding these three callbacks, and the MyPluginProcess class, our example code looks like this. Note that the RenderTasks() callback still needs code to allow it to interact with the running process launched in the StartJob() callback.

from Deadline.Plugins import *

######################################################################
## This is the function that Deadline calls to get an instance of the
## main DeadlinePlugin class.
######################################################################
def GetDeadlinePlugin():
    return MyPlugin()

######################################################################
## This is the function that Deadline calls when the plugin is no
## longer in use so that it can get cleaned up.
######################################################################
def CleanupDeadlinePlugin( deadlinePlugin ):
    deadlinePlugin.Cleanup()

######################################################################
## This is the main DeadlinePlugin class for MyPlugin.
######################################################################
class MyPlugin (DeadlinePlugin):

    ## Variable to hold the Managed Process object.
    Process = None

    ## Hook up the callbacks in the constructor.
    def __init__( self ):
        self.InitializeProcessCallback += self.InitializeProcess
        self.StartJobCallback += self.StartJob
        self.RenderTasksCallback += self.RenderTasks
        self.EndJobCallback += self.EndJob

    ## Clean up the plugin.
    def Cleanup():
        del self.InitializeProcessCallback
        del self.StartJobCallback
        del self.RenderTasksCallback
        del self.EndJobCallback

        # Clean up the managed process object.
        if self.Process:
            self.Process.Cleanup()
            del self.Process

    ## Called by Deadline to initialize the process.
    def InitializeProcess( self ):
        # Set the plugin specific settings.
        self.SingleFramesOnly = False
        self.PluginType = PluginType.Advanced

    ## Called by Deadline when the job starts.
    def StartJob( self ):
        myProcess = MyPluginProcess()
        StartMonitoredManagedProcess( "My Process", myProcess )

    ## Called by Deadline for each task the Slave renders.
    def RenderTasks( self ):
        # Do something to interact with the running process.
        pass

    ## Called by Deadline when the job ends.
    def EndJob( self ):
        ShutdownMonitoredManagedProcess( "My Process" )

######################################################################
## This is the ManagedProcess class that is launched above.
######################################################################
class MyPluginProcess (ManagedProcess):
    deadlinePlugin = None

    ## Hook up the callbacks in the constructor.
    def __init__( self, deadlinePlugin ):
        self.InitializeProcessCallback += self.InitializeProcess
        self.RenderExecutableCallback += self.RenderExecutable
        self.RenderArgumentCallback += self.RenderArgument

    ## Clean up the managed process.
    def Cleanup():
        # Clean up stdout handler callbacks.
        for stdoutHandler in self.StdoutHandlers:
            del stdoutHandler.HandleCallback

        del self.InitializeProcessCallback
        del self.RenderExecutableCallback
        del self.RenderArgumentCallback

    ## Called by Deadline to initialize the process.
    def InitializeProcess( self ):
        # Set the ManagedProcess specific settings.
        self.ProcessPriority = ProcessPriorityClass.BelowNormal
        self.UseProcessTree = True
        self.StdoutHandling = True
        self.PopupHandling = True

        # Set the stdout handlers.
        self.AddStdoutHandlerCallback(
            "WARNING:.*" ).HandleCallback += self.HandleStdoutWarning
        self.AddStdoutHandlerCallback(
            "ERROR:(.*)" ).HandleCallback += self.HandleStdoutError

        # Set the popup ignorers.
        self.AddPopupIgnorer( "Popup 1" )
        self.AddPopupIgnorer( "Popup 2" )

        # Set the popup handlers.
        self.AddPopupHandler( "Popup 3", "OK" )
        self.AddPopupHandler( "Popup 4", "Do not ask me this again;Continue" )

    ## Callback for when a line of stdout contains a WARNING message.
    def HandleStdoutWarning( self ):
        self.deadlinePlugin.LogWarning( self.GetRegexMatch(0) )

    ## Callback for when a line of stdout contains an ERROR message.
    def HandleStdoutError( self ):
        self.deadlinePlugin.FailRender( "Detected an error: " + self.GetRegexMatch(1) )

    ## Callback to get the executable used for rendering.
    def RenderExecutable( self ):
        return self.deadlinePlugin.GetConfigEntry( "MyPluginRenderExecutable" )

    ## Callback to get the arguments that will be passed to the executable.
    def RenderArgument( self ):
        arguments = " -verbose " + self.deadlinePlugin.GetPluginInfoEntry( "Verbose" )
        arguments += " -scene \"" + self.deadlinePlugin.GetDataFilename() + "\""
        return arguments

Because the Advanced plug-ins are much more complex than the Simple plug-ins, we recommend taking a look at the following plug-ins that are shipped with Deadline for examples:

  • 3dsmax
  • Fusion
  • Lightwave
  • MayaBatch
  • Modo
  • Nuke
  • SoftimageBatch

Migrating Plug-ins from Deadline 5

Some changes were made to the Scripting API in Deadline 6, which means that Deadline 6 and later are NOT backward compatible with plugin scripts written for Deadline 5. However, migrating your scripts over is relatively straightforward, and this guide will walk you through the API changes so that you can update your scripts as necessary.

Global Functions

In Deadline 6, all global API functions were removed, and replaced with DeadlinePlugin member functions, or with static utility functions. See the Migrating Scripts From Deadline 5 section in the Scripting Overview documentation for more information, including replacement functions.

Almost all plugin-specific global functions are now DeadlinePlugin member functions. For example, the global ‘LogInfo( message )’ function has been replaced with a member function for the DeadlinePlugin class, which you created in your event python file. So instead of:

LogInfo( "this is a test message" )

You would use this code:

self.LogInfo( "this is a test message" )

The only functions that aren’t DeadlinePlugin member functions are listed below, along with their replacement utility functions.

Original Global Function Replacement Function
CheckPathMapping( path ) RepositoryUtils.CheckPathMapping( path )
CheckPathMappingInFile( inFileName, outFileName ) RepositoryUtils.CheckPathMappingInFile( inFileName, outFileName )
CheckPathMappingInFileAndReplaceSeparator( inFileName, outFileName , separatorToReplace, newSeparator ) RepositoryUtils.CheckPathMappingInFileAndReplaceSeparator( inFileName, outFileName, separatorToReplace, newSeparator )
PathMappingRequired( path ) RepositoryUtils.PathMappingRequired( path )

Callbacks

You need to set up callbacks in the constructor of your DeadlinePlugin class that you created in your plugin python file. Examples are shown in the documentation above, and you can look at the plug-ins that ship with Deadline for references as well. For example:

def __init__( self ):
    self.InitializeProcessCallback += self.InitializeProcess
    self.RenderExecutableCallback += self.RenderExecutable
    self.RenderArgumentCallback += self.RenderArgument
    self.PreRenderTasksCallback += self.PreRenderTasks
    self.PostRenderTasksCallback += self.PostRenderTasks

Note that these callbacks need to be manually cleaned up when the plug-in is no longer in use. See the documentation regarding the CleanupDeadlinePlugin function above for more information.

Deprecated Mode

As mentioned above, you can set the DeprecatedMode property in your dlinit file to True. This mode allows Python.NET plug-ins written for Deadline 5.1 or 5.2 to work with Deadline 6 and later, which can make the transition to Deadline 6 easier if you have custom plug-ins.

Note that when DeprecatedMode is enabled, all global functions will still be available, so if you have custom Python.NET plug-ins, you just need to drop them in the ‘custom/plugins’ folder in the Repository, and add “DeprecatedMode=True” to your dlinit file.

If you have custom IronPython plug-ins from Deadline 5.2 or earlier, they will not work with Deadline 6 and later.