Run Maya and execute a script

How can I execute a python scripts after I launch Maya with subprocess

subprocess.Popen('C:/maya/maya.exe')

I need Maya with GUI. And the Python script I need to run is not static (and I don`t need to execute something all the time I run Maya), so I don’t think I can utilize userSetup.py.

I need to create/open asset/shot scenes from an external app, like Shotgun.

So one option is to use the -command flag when launching maya, to run a mel command probably something like

subprocess.Popen(['C:/maya/maya.exe', '-command', "python('execfile(\"path_to_your_script\"')"])

Another would be to have a startup script that opens up the commandPort and then you can squirt commands through that to the running process.


has a pretty good example on how to set that up for sublime.

1 Like

I can execute python code directly (without saving it to a file), right? Did not work for me:

cmd = [maya_exe, '-hideConsole', '-command', "python('print 'OLA' ')"]
subprocess.Popen(cmd)

// Error: python('print 'OLA' '); // 
// Error: Line 1.8: Syntax error //

Try:
cmd = [maya_exe, '-hideConsole', '-command', "python(\"print 'OLA' \")"]

Mel basically has no idea about single quotes, so when passing in the python string you want it to eval, you have to wrap it in double quotes.

1 Like

What if I need to pass an object (instance of a custom class)? It does not work, produce an error.

command = 'python(\"from maya_tools import asset_create; '\
                  'asset_create.run({}, {}) \")'\
                  .format(file_type_dict, asset_object)

cmd = [maya_exe, '-hideConsole',  '-command',  command]   
subprocess.Popen(cmd)

However, dictionaries, strings, integers are working fine.

The way we’ve been handling that at work is by pickling. pickle is python’s generic object representation library, and allows you to easily write almost any object out to a file. https://docs.python.org/2.7/library/pickle.html

So gather all your data and pickle it in a temporary file. Then build the command for maya to unpickle that file, and pass that data to whatever function you want.

[Edit]
The biggest caveat is you have to make sure that whatever classes you have created are available using the same import statement both in and out of maya.

2 Likes

There’s a couple of ways around the nasty encoding issues that come with trying to run a commandline maya or mayapy.

Python support was bolted on after the fact so you have to indirect through mel. Rather than packing too much encoding into a command line (where it’s really easy to fatfinger something like a nested string encoding) one good trick is just to generate a mel file (temporary or permanent ) and pass that to the maya.

Keystone packs any arbitrary python you want into a mel file which can be run from the commandline. It’s designed for distributing an entire toolkit with multiple files, but it’d work for a single script if you wanted to do that.

If you have a simpler need to just pass some Python to maya without all the hassles, you can just steal the operative part of that like so:

import base64
import subprocess


def launch_maya(maya_path, python_script, *additional_args):

    encoded_python = base64.b64encode(python_script.encode('utf-8'))
    script_text = '''python("import base64; exec (base64.urlsafe_b64decode({}))")'''
    args = [maya_path, '-c', script_text.format(encoded_python)]
    args.extend(str(i) for i in additional_args)
    subprocess.check_call(args)


launch_maya('c:/program files/autodesk/maya2018/bin/maya.exe', 'import sys; print("hello world " * int(sys.argv[-1]))', 7)

In this version python_script is the text of whatever python you want to run; you could just read it from a file if you wanted to.

If you pass additional arguments to the command line they show up in sys.argv so you can get at them (as in the example above) – they will all be strings, but you can pass any string you want.

If you plan on reusing the same script, you could convert the above function to generate a mel script that included the encoded python – that would make it double-clickable. That’s how Keystone works.

4 Likes

Hi there,

This problem seems to have different solutions depending on how the command is launched.
Is there a reliable way to do this?

This will work in cmd.exe but not with subprocess.Popen(command, shell=True):

maya.exe -hideConsole -command python(\"print('TEST')\")
 
RESULT using cmd.exe:
python("print('TEST')")

RESULT using subprocess:
python(print('TEST'))

This work subprocess but not in cmd.exe:

maya.exe -hideConsole -command python(\\\"print('TEST')\\\")

RESULT using cmd.exe:
python(\"print('TEST')\")

RESULT using subprocess:
python("print('TEST')")

This doesnt seem to work in any case:

maya.exe -hideConsole -command python(""print('TEST')"")
RESULT:
python(print('TEST'))
Error: line 1: invalid syntax #

I tried @Theodox answer with base64 and his launch_maya() function, but I can’t seem to make it work, here’s the result once maya has started:

python("import base64; exec (base64.urlsafe_b64decode(aW1wb3J0IHN5czsgcHJpbnQoImhlbGxvIHdvcmxkICIgKiBpbnQoc3lzLmFyZ3ZbLTFdKSk=))");
 Error: line 1: invalid syntax #

Is there a reliable way to do this using subprocess.Popen and cmd.exe without modifying the command?

Another problem:
This work:

maya.exe -hideConsole -command python(\\\"print('TEST')\\\")
RESULT
TEST

This does not:

maya.exe -hideConsole -command python(\\\"from maya import cmds\\\")
// Error: python("from; // 
// Error: Line 1.14: Unterminated string. //

I don’t understand the difference between the two lines. Why would the second command not work when the first one does?

Looking at your error it seems like my example left out single quotes around the argument to urlsafe_b64decode. Does it work if that line becomes

    script_text = '''python("import base64; exec (base64.urlsafe_b64decode('{}'))")'''

Throwing my solution into the ring - I’ve found it easier to generate a new local command script and direct maya to it at startup, than to pass multi-line commands directly to the -command flag at startup:

		scriptPath = r'C:my/local/scratch/script/path.py'
		
		# source, compile, and execute script file
		pythonCmd = fr"""with open(r'{scriptPath}', 'r') as f:exec(compile(f.read(), 'startupScript', 'exec'), {"{}"}, {"{}"})""" # horrible but it works

		# begin maya process
		newProcess = subprocess.Popen(
			(mayaExePath, 
			"-command", 
			fr"""python("{pythonCmd}")""", # horrible but it works
			"-noAutoloadPlugins"
			 ),
			shell=True,
		)

In true software development style, this works and I’m never touching it again.

The separate script file also has the advantage of being easier to debug than a command that is only ever put together at the point of use.
It also lets us focus on the issue of formatting any arbitrary commands to a python script, rather than the additional constraint of that script passing gracefully through the MEL layer - if there are any kind of string or dict literals involved, I’ve found this to be a proper nightmare.